2008-11-19 5 views
7

Nous avons un certain nombre d'arithmétiques entiers qui, pour des raisons historiques, doivent fonctionner de la même manière sur PHP que dans quelques langages statiquement typés. Depuis la dernière mise à jour de PHP, le comportement des entiers débordant a changé. En fait, nous utilisons la formule suivante:Forcer le débordement d'entier PHP

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

Cependant, même avec des conversions:

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

Je termine encore avec le nombre complètement faux ...

Par exemple, avec $ x1 = -1580033017, $ x2 = -2072974554, $ x3 = -1170476976) et $ x4 = -1007518822, je me retrouve avec -30512150 en PHP et 1617621783 en C#.

Juste additionnant $ x1 et x2 $ Je ne peux pas obtenir la bonne réponse:

En C# Je reçois

(-1580033017 + -2072974554) = 641959725 

En PHP:

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

qui est le même que:

intval(-1580033017 + -2072974554) = -2147483648 

Cela ne me dérange pas d'écrire une fonction "IntegerOverflowAdd" ou quelque chose, mais je n'arrive pas à comprendre comment (-1580033017 + -2072974554) est égal à 641959725. (Je reconnais que c'est -2147483648 + (2 * 2^31), mais -2147483648 + 2^31 est -1505523923 qui est supérieur à Int.Min alors pourquoi est-ce que vous ajoutez 2 * 2^31 et non 2^31?)

Toute aide serait appréciée ...

Répondre

13

J'ai donc résolu le problème et découvert beaucoup de choses sur PHP (au moins dans la façon dont il gère le débordement d'entier).

1) Cela dépendait complètement du croisement entre la plate-forme sur laquelle la machine tournait, quelle version de PHP, avec ou sans PHP Suhosin Hardened, et le nombre de bits pour lesquels elle a été compilée (32 ou 64). 6 machines se comportaient comme je m'y attendais (ce qui était faux, du moins selon leur documentation) et 3 machines se comportaient d'une manière que je ne peux toujours pas expliquer, et 3 machines se comportaient selon ce que la commande intval dit dans le Documentation.

2) Intval est censé retourner PHP_MAX_INT lorsque int> PHP_MAX_INT (pas int & 0xffffffff), mais cela ne se produit que sur certaines versions de PHP4 et PHP5. Différentes versions de PHP retournent des valeurs différentes quand int> PHP_MAX_INT.

3) Le code suivant peut revenir 3 résultats différents (voir 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"; 
?> 

Il peut revenir (ce qui semble être bon pour INTVAL mais mal pour & 0xffffff)

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

Et il peut retourner (ce qui contredit la documentation PHP pour intval):

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

Et sur les machines 64 bits, il retourne (ce qui est correct):

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

Solution

Quoi qu'il en soit, je besoin d'une solution qui fonctionne sur toutes ces plates-formes, et ne dépendent pas de bizarreries d'une version particulière de PHP compilé avec un int Max particulier.Ainsi, je cam avec la fonction cross-PHP thirtyTwoBitIntval suivante:

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

Commentaire

je pense que les concepteurs de PHP aurait dû dire un Int est un 32 bit Int peu importe si elle est en cours d'exécution sur une machine 32 ou 64 ou 128 bits (comme le DotNet CLR par exemple), et ne l'a pas converti aléatoirement en un flottant en fonction du nombre de bits que PHP est en dessous du compilateur.

+1

Je pense que vous voulez dire PHP_INT_MAX, pas PHP_MAX_INT . – scotts

2

Je pense que cela peut avoir à faire avec l'entier en PHP étant 32 bits non signé comme en C# ils sont signés 32 bits par défaut.

Vous jouez avec des nombres sur le bord de la plage normale de 31 à 32 bits.

S'il vous plaît voir la documentation supplémentaire dans le manuel PHP:

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

La taille d'un entier dépend de la plate-forme, même si une valeur maximale d'environ deux milliards est la valeur habituelle (qui est 32 bits signés) . PHP ne supporte pas les entiers non signés. La taille de l'entier peut être déterminée en utilisant la constante PHP_INT_SIZE, et la valeur maximale en utilisant la constante PHP_INT_MAX depuis PHP 4.4.0 et PHP 5.0.5.

2

Est-ce que cela fonctionnera?

echo (-1580033017 + -2072974554) & 0xffffffff 

Généraliser, vous pouvez faire (pardonner les erreurs de syntaxe, je ne l'ai pas touché PHP depuis longtemps):

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

interne, PHP utilise un type « entier » pour la plupart des numéros . Cependant, ceux-ci vont seulement jusqu'à présent: si vous ajoutez un grand entier à un grand entier, PHP verra que le résultat est trop grand pour tenir dans un entier normal et l'assignera à un nombre à virgule flottante. Les nombres à virgule flottante (flottants) eux-mêmes ne vont pas si haut, cependant, et il y a un point autour de la marque à seize chiffres où PHP va juste perdre complètement l'intrigue.

Il existe une option permettant d'utiliser des mathématiques de précision arbitraire, prenant en charge des nombres de n'importe quelle taille et précision, représentés par des chaînes. Voir plus ici: http://us2.php.net/bc

1

Vérifiez votre numéro de version de PHP - Je crois qu'il est possible que vous obtiendrez des résultats différents avec différentes versions de PHP qui peuvent avoir un support différent pour les entiers longs. Je crois qu'il y avait un bug avec des entiers longs dans au moins l'une des versions de PHP 5.

Dans la version PHP 5.2.0 - la réponse est la même que vous avez en C#

1617621783,

utilisant la fonction exacte que vous avez ci-dessus.

Vous pouvez utiliser la commande phpinfo() pour trouver facilement votre numéro de version.

$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 vous voulez avoir 100% de solution de travail 32 bits intval fois sur 32 et 64 plates-formes de bits, alors je vous suggère d'utiliser la solution suivante:

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

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

    return $value; 
} 
Questions connexes