2008-11-19 15 views
7

Tenemos una aritmética de números enteros que por razones históricas tiene que funcionar igual en PHP que en algunos lenguajes estáticos. Desde la última vez que actualizamos PHP, el comportamiento de los enteros desbordados ha cambiado. Básicamente estamos utilizando la fórmula siguiente:Forzar desbordamiento entero de PHP

function f($x1, $x2, $x3, $x4) 
{ 
    return (($x1 + $x2)^$x3) + $x4; 
} 

Sin embargo, incluso con las conversiones:

function f($x1, $x2, $x3, $x4) 
{ 
    return intval(intval(intval($x1 + $x2)^$x3) + $x4); 
} 

Todavía estoy terminando con el número completamente equivocado ...

Por ejemplo, con $ x1 = -1580033017, $ x2 = -2072974554, $ x3 = -1170476976) y $ x4 = -1007518822, termino con -30512150 en PHP y 1617621783 en C#.

Sólo la suma de $ x1 y $ x2 No puedo obtener la respuesta correcta:

En C# consigo

(-1580033017 + -2072974554) = 641959725 

En PHP:

intval(intval(-1580033017) + intval(-2072974554)) = -2147483648 

que es lo mismo que:

intval(-1580033017 + -2072974554) = -2147483648 

No me importa escribir una función "IntegerOverflowAdd" o algo así, pero no puedo entender cómo (-1580033017 + -2072974554) es igual a 641959725. (Reconozco que es -2147483648 + (2 * 2^31)), pero -2147483648 + 2^31 es -1505523923 que es mayor que Int.Min así que ¿por qué se agregan 2 * 2^31 y no 2^31?)

Cualquier ayuda sería apreciada ...

Respuesta

13

Así que resolvió el problema, y ​​descubrió mucho acerca de PHP (por lo menos en la forma en que maneja Desbordamiento de enteros).

1) Depende completamente de la plataforma en la que se ejecutaba la máquina, qué versión de PHP, si tenía Suhosin Hardened PHP en ejecución o no, y cuántos bits compilaba (32 o 64). 6 máquinas se comportaron de la forma esperada (que en realidad era incorrecta, al menos incorrecta según su documentación) y 3 máquinas se comportaron de una manera que aún no puedo explicar, y 3 máquinas se comportaron de acuerdo con lo que el comando intval dice que hace en el documentación.

2) Se supone que Intval debe devolver PHP_MAX_INT cuando int> PHP_MAX_INT (no int & 0xffffffff), pero esto solo ocurre en algunas versiones de PHP4 y PHP5. Las diferentes versiones de PHP devuelven diferentes valores cuando int> PHP_MAX_INT.

3) El siguiente código puede devolver 3 resultados diferentes (véase 1):

<?php 
echo "Php max int: ".PHP_INT_MAX."\n"; 
echo "The Val: ".(-1580033017 + -2072974554)."\n"; 
echo "Intval of the val: ".intval(-3653007571)."\n"; 
echo "And 0xffffffff of the val: ".(-3653007571 & 0xffffffff)."\n"; 
?> 

puede devolver (que parece ser el adecuado para intval pero equivocada para & 0xffffff)

Php max int: 2147483647 
The Val: -3653007571 
Intval of the val: -2147483648 
And of the val: -2147483648 

Y puede volver (que contradice la documentación de PHP para intval):

Php max int: 2147483647 
The Val: -3653007571 
Intval of the val: -641959725 
And of the val: -641959725 

Y en 64 máquinas de bits que devuelve (que es correcta):

Php max int: 2147483647 
The Val: -3653007571 
Intval of the val: -3653007571 
And of the val: -641959725 

solución

De todos modos, yo necesitaba una solución que funciona en todas estas plataformas, y no ser dependiente de los caprichos de una versión particular de PHP compilado con un particular int Max.Por lo tanto me leva con la siguiente función thirtyTwoBitIntval cruzada PHP:

function thirtyTwoBitIntval($value) 
{ 
    if ($value < -2147483648) 
    { 
     return -(-($value) & 0xffffffff); 
    } 
    elseif ($value > 2147483647) 
    { 
     return ($value & 0xffffffff); 
    } 
    return $value; 
} 

comentario

sí creo que los diseñadores de PHP debería haber dicho un Int es un 32 Bit Int no importa si se está ejecutando en una máquina de 32 o 64 o 128 bits (como la CLR de DotNet, por ejemplo), y no la convirtió aleatoriamente a una flotación en función de la cantidad de bits en los que PHP es compilador.

+1

Creo que te refieres PHP_INT_MAX, no PHP_MAX_INT . – scotts

2

Creo que puede tener que ver con el número entero en PHP que tiene 32 bits sin signo, ya que en C# tienen 32 bits por defecto.

Estás jugando con números en el borde del rango normal de 31-32 bits.

favor ver documentación adicional en el manual de PHP:

http://www.php.net/manual/en/language.types.integer.php

El tamaño de un entero es dependiente de la plataforma, aunque un valor máximo de aproximadamente dos mil millones es el valor usual (que es 32 bits firmados) . PHP no admite enteros sin signo. El tamaño entero se puede determinar usando la constante PHP_INT_SIZE, y el valor máximo usando la constante PHP_INT_MAX desde PHP 4.4.0 y PHP 5.0.5.

2

¿Funcionará?

echo (-1580033017 + -2072974554) & 0xffffffff 

Para generalizar, se puede hacer (perdón por los errores de sintaxis, no he tocado PHP durante mucho tiempo):

function s32add($a, $b) { 
    return ($a + $b) & 0xffffffff; 
} 
3

Internamente, PHP utiliza un tipo de "entero" para la mayoría de los números . Sin embargo, estos solo van tan lejos: si agrega un número entero grande a un número entero grande, PHP verá que el resultado es demasiado grande para caber en un entero normal y lo asignará a un número de punto flotante. Sin embargo, los números de coma flotante (flotantes) solo llegan a ser tan altos, y hay un punto alrededor de la marca de dieciséis dígitos donde PHP perderá la trama por completo.

Existe la opción de utilizar matemáticas de precisión arbitraria que admite números de cualquier tamaño y precisión, representados como las cadenas. Vea más aquí: http://us2.php.net/bc

1

Compruebe su número de versión de PHP - Creo que es posible que obtenga diferentes resultados con diferentes versiones de PHP que pueden tener soporte diferente para enteros largos. Creo que hubo un error con enteros largos en al menos una de las versiones de PHP 5.

En la versión de PHP 5.2.0 - la respuesta es exactamente el mismo que el que tienes en C#

1617621783,

utilizando la función exacta que tiene encima.

Puede usar el comando phpinfo() para encontrar su número de versión, fácilmente.

$x1 = -1580033017; 
$x2 = -2072974554; 
$x3 = -1170476976 ; 
$x4 = -1007518822; 
echo f($x1, $x2, $x3, $x4); 

function f($x1, $x2, $x3, $x4) 
{ 
    return intval(intval(intval($x1 + $x2)^$x3) + $x4); 
} 
11

Si usted quiere tener la solución de trabajo del 100% para 32 bits intval tanto en 32 y 64 plataformas de bits, entonces le sugiero que utilice la siguiente solución:

function intval32bits($value) 
{ 
    $value = ($value & 0xFFFFFFFF); 

    if ($value & 0x80000000) 
     $value = -((~$value & 0xFFFFFFFF) + 1); 

    return $value; 
} 
Cuestiones relacionadas