Pregunta

Bien, entonces PHP no es el mejor lenguaje para tratar con números enteros arbitrariamente grandes, considerando que solo admite de forma nativa enteros con signo de 32 bits.Sin embargo, lo que estoy tratando de hacer es crear una clase que pueda representar un número binario arbitrariamente grande y poder realizar operaciones aritméticas simples en dos de ellos (suma/resta/multiplicación/división).

Mi objetivo es tratar con números enteros de 128 bits.

Hay un par de enfoques que estoy analizando y veo problemas en ellos.Cualquier aportación o comentario sobre lo que elegiría y cómo podría hacerlo sería muy apreciado.

Enfoque #1: Cree una clase entera de 128 bits que almacene su número entero internamente como cuatro enteros de 32 bits.El único problema con este enfoque es que no estoy seguro de cómo manejar los problemas de desbordamiento/desbordamiento insuficiente al manipular fragmentos individuales de los dos operandos.

Enfoque #2: Utilice la extensión bcmath, ya que parece algo para lo que fue diseñado.Mi única preocupación al adoptar este enfoque es la configuración de escala de la extensión bcmath, porque no puede haber errores de redondeo en mis números enteros de 128 bits;deben ser precisos.También me preocupa poder convertir eventualmente el resultado de las funciones bcmath en una cadena binaria (que luego tendré que insertar en algunas funciones de cifrado mcrypt).

Enfoque #3: Guarde los números como cadenas binarias (probablemente primero LSB).En teoría, debería poder almacenar números enteros de cualquier tamaño arbitrario de esta manera.Todo lo que tendría que hacer es escribir las cuatro funciones aritméticas básicas para realizar add/sub/mult/div en dos cadenas binarias y producir un resultado de cadena binaria.Este es exactamente el formato que también necesito entregar a mcrypt, por lo que es una ventaja adicional.Este es el enfoque que creo que es más prometedor en este momento, pero el único punto conflictivo que tengo es que PHP no me ofrece ninguna forma de manipular los bits individuales (que yo sepa).Creo que tendría que dividirlo en fragmentos del tamaño de un byte (sin juego de palabras), momento en el que se aplican mis preguntas sobre el manejo del desbordamiento/desbordamiento insuficiente del Enfoque n.º 1.

¿Fue útil?

Solución

El Extensión PHP GMP Será mejor para esto.Como beneficio adicional, puedes usarlo para realizar tu conversión de decimal a binario, así:

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

Otros consejos

ya hay varios clases disponible Para esto, es posible que desee verlos antes de escribir su propia solución (si es que aún es necesario escribir su propia solución).

Por lo que puedo decir, la extensión bcmath es la que querrás.Los datos en el manual de PHP son un poco escasos, pero usted puede establecer la precisión para que sea exactamente lo que necesita usando la función bcscale(), o el tercer parámetro opcional en la mayoría de las otras funciones bcmath.No estoy muy seguro de lo de las cadenas binarias, pero un poco de búsqueda en Google me dice que deberías poder utilizar la función pack().

Implementé lo siguiente Evaluador de denuncia PEMDAS BC que te puede resultar útil.

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;
}

Se ocupa automáticamente de los errores de redondeo, simplemente establezca la precisión en los dígitos que necesite.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top