Domanda

Ok, quindi PHP non è il linguaggio migliore per gestire numeri interi arbitrariamente grandi, considerando che supporta nativamente solo numeri interi con segno a 32 bit.Quello che sto cercando di fare però è creare una classe che possa rappresentare un numero binario arbitrariamente grande ed essere in grado di eseguire semplici operazioni aritmetiche su due di essi (addizione/sottrazione/moltiplicazione/divisione).

Il mio obiettivo ha a che fare con numeri interi a 128 bit.

Ci sono un paio di approcci che sto esaminando e i problemi che vedo con loro.Qualsiasi input o commento su ciò che sceglieresti e su come potresti procedere sarebbe molto apprezzato.

Approccio n. 1: Crea una classe di numeri interi a 128 bit che memorizza internamente il suo numero intero come quattro numeri interi a 32 bit.L'unico problema con questo approccio è che non sono sicuro di come gestire i problemi di overflow/underflow quando si manipolano singoli blocchi dei due operandi.

Approccio n. 2: Usa l'estensione bcmath, poiché sembra qualcosa per cui è stato progettato.La mia unica preoccupazione nell'adottare questo approccio è l'impostazione della scala dell'estensione bcmath, perché non possono esserci errori di arrotondamento nei miei interi a 128 bit;devono essere precisi.Sono anche preoccupato di riuscire eventualmente a convertire il risultato delle funzioni bcmath in una stringa binaria (che in seguito dovrò inserire in alcune funzioni di crittografia di mcrypt).

Approccio n. 3: Memorizza i numeri come stringhe binarie (probabilmente prima LSB).Teoricamente dovrei essere in grado di memorizzare numeri interi di qualsiasi dimensione arbitraria in questo modo.Tutto quello che dovrei fare è scrivere le quattro funzioni aritmetiche di base per eseguire add/sub/mult/div su due stringhe binarie e produrre un risultato di stringa binaria.Questo è esattamente il formato che devo consegnare anche a mcrypt, quindi è un ulteriore vantaggio.Questo è l'approccio che penso sia il più promettente al momento, ma l'unico punto critico che ho è che PHP non mi offre alcun modo per manipolare i singoli bit (che io sappia).Credo che dovrei suddividerlo in blocchi delle dimensioni di un byte (nessun gioco di parole), a quel punto si applicano le mie domande sulla gestione dell'overflow/underflow dall'approccio n. 1.

È stato utile?

Soluzione

IL Estensione GMP PHP sarà meglio per questo.Come bonus aggiuntivo, puoi usarlo per eseguire la conversione da decimale a binario, in questo modo:

gmp_strval(gmp_init($n, 10), 2);

Altri suggerimenti

Ce ne sono già diversi classi disponibile per questo motivo potresti voler guardarli prima di scrivere la tua soluzione (se effettivamente scrivere la tua soluzione è ancora necessario).

Per quanto ne so, l'estensione bcmath è quella che vorrai.I dati nel manuale PHP sono un po' scarsi, ma puoi impostare la precisione in modo che sia esattamente ciò di cui hai bisogno utilizzando la funzione bcscale() o il terzo parametro opzionale nella maggior parte delle altre funzioni bcmath.Non sono troppo sicuro per quanto riguarda le stringhe binarie, ma un po' di ricerca su Google mi dice che dovresti essere in grado di farlo utilizzando la funzione pack().

Ho implementato quanto segue Valutatore BC del reclamo PEMDAS che potrebbe esserti utile.

function BC($string, $precision = 32)
{
    if (extension_loaded('bcmath') === true)
    {
        if (is_array($string) === true)
        {
            if ((count($string = array_slice($string, 1)) == 3) && (bcscale($precision) === true))
            {
                $callback = array('^' => 'pow', '*' => 'mul', '/' => 'div', '%' => 'mod', '+' => 'add', '-' => 'sub');

                if (array_key_exists($operator = current(array_splice($string, 1, 1)), $callback) === true)
                {
                    $x = 1;
                    $result = @call_user_func_array('bc' . $callback[$operator], $string);

                    if ((strcmp('^', $operator) === 0) && (($i = fmod(array_pop($string), 1)) > 0))
                    {
                        $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string = array_shift($string), $x, $i = pow($i, -1)));

                        do
                        {
                            $x = $y;
                            $y = BC(sprintf('((%1$s * %2$s ^ (1 - %3$s)) / %3$s) - (%2$s / %3$s) + %2$s', $string, $x, $i));
                        }

                        while (BC(sprintf('%s > %s', $x, $y)));
                    }

                    if (strpos($result = bcmul($x, $result), '.') !== false)
                    {
                        $result = rtrim(rtrim($result, '0'), '.');

                        if (preg_match(sprintf('~[.][9]{%u}$~', $precision), $result) > 0)
                        {
                            $result = bcadd($result, (strncmp('-', $result, 1) === 0) ? -1 : 1, 0);
                        }

                        else if (preg_match(sprintf('~[.][0]{%u}[1]$~', $precision - 1), $result) > 0)
                        {
                            $result = bcmul($result, 1, 0);
                        }
                    }

                    return $result;
                }

                return intval(version_compare(call_user_func_array('bccomp', $string), 0, $operator));
            }

            $string = array_shift($string);
        }

        $string = str_replace(' ', '', str_ireplace('e', ' * 10 ^ ', $string));

        while (preg_match('~[(]([^()]++)[)]~', $string) > 0)
        {
            $string = preg_replace_callback('~[(]([^()]++)[)]~', __FUNCTION__, $string);
        }

        foreach (array('\^', '[\*/%]', '[\+-]', '[<>]=?|={1,2}') as $operator)
        {
            while (preg_match(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), $string) > 0)
            {
                $string = preg_replace_callback(sprintf('~(?<![0-9])(%1$s)(%2$s)(%1$s)~', '[+-]?(?:[0-9]++(?:[.][0-9]*+)?|[.][0-9]++)', $operator), __FUNCTION__, $string, 1);
            }
        }
    }

    return (preg_match('~^[+-]?[0-9]++(?:[.][0-9]++)?$~', $string) > 0) ? $string : false;
}

Si occupa automaticamente degli errori di arrotondamento, basta impostare la precisione su qualsiasi cifra necessaria.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top