57 votes

Travailler avec de grands nombres en PHP

Pour utiliser exponentiation modulaire comme vous le feriez en utilisant l'option Test de primauté de Fermat avec de grands nombres (100 000+), cela demande de très grands calculs.

Lorsque je multiplie deux grands nombres (par exemple 62574 et 62574), PHP semble convertir le résultat en un flottant. Obtenir la valeur du modulus renvoie des valeurs étranges.

$x = 62574 * 62574;
var_dump($x);          // float(3915505476) ... correct
var_dump($x % 104659); // int(-72945)  ... wtf.

Existe-t-il un moyen de faire en sorte que PHP effectue ces calculs correctement ? Sinon, existe-t-il une autre méthode pour trouver les valeurs du module qui fonctionne pour les grands nombres ?

0 votes

Note : comme vous pouvez le voir dans le manuel officiel de PHP, dans les commentaires c'est parce que % utilise une enveloppe pour les entiers.

0 votes

L'implémentation des nombres entiers de PHP est fatalement défectueuse dans la mesure où 1) elle est totalement dépendante de la plate-forme (endianess et taille des bits) 2) PHP n'utilise que des nombres entiers SIGNÉS et 3) si l'on sort de la plage des nombres entiers signés, la partie fatale entre en jeu, car elle sera convertir ce résultat en un flottant. Cela signifie que si vous faites 1 + 2147483647 sur un système 32 bits, vous obtiendrez un flottant, ce qui rend le conditionnement des données binaires "vraiment intéressant".

0 votes

Les utilisateurs modernes peuvent constater qu'ils ne peuvent pas reproduire ce comportement en utilisant les chiffres de la question sur leurs machines modernes et brillantes à 64 bits. var_dump($x) ils obtiennent un int et non un float . Cependant, s'ils essaient de faire $x = PHP_INT_MAX + 1 au lieu de $x = 62574 * 62574; ils seront en mesure de reproduire le reste de la folie avec succès.

54voto

Ivan Krechetov Points 6465

Pour une raison quelconque, il y a deux bibliothèques standard en PHP qui gèrent les nombres de longueur/précision arbitraire : BC Math y BPF . Je préfère personnellement le GMP, car il est plus frais et possède une API plus riche.

Sur la base des BPF, j'ai mis en place Classe Decimal2 pour le stockage et le traitement des montants en devises (comme 100,25 USD). Un lot de calculs modulaires sans aucun problème. Testé avec très de grands nombres.

3 votes

GMP ne concerne que les nombres entiers, tandis que BC Math concerne les nombres à virgule flottante.

1 votes

@Fleshgrinder Non, BC Math supporte la virgule fixe à précision arbitraire. Les nombres à virgule flottante sont entièrement différents.

0 votes

Vous avez tout à fait raison, oui. Le commentaire ci-dessus était un commentaire rapide où je voulais simplement dire GMP == int && BC == float . :)

50voto

K.Sya Points 73

Utiliser ce

 $num1 = "123456789012345678901234567890";
 $num2 = "9876543210";
 $r    = mysql_query("Select @sum:=$num1 + $num2");
 $sumR = mysql_fetch_row($r);
 $sum  = $sumR[0];

52 votes

Désolé, mais le serveur SQL est un serveur SQL, pas une calculatrice.

5 votes

@GordonM, n'avez-vous jamais trié des chaînes de caractères en utilisant ListBox dans Delphi ?

6 votes

@Namek Vu la popularité de Delphi, je doute que quelqu'un l'ait fait au cours des 10 dernières années.

21voto

Owen Points 36009

Avez-vous jeté un coup d'oeil à bcmod() ? php a des problèmes avec les entiers supérieurs à 2^31 - 1 sur les plateformes 32 bits.

var_dump(bcmod("$x", '104659') ); // string(4) "2968"

4voto

Yuval F Points 15248

Je vous suggère d'essayer BigInteger . Si cela ne fonctionne pas, vous pouvez utiliser SWIG pour ajouter du code C/C++ pour les calculs de grands entiers et le lier à votre code.

2 votes

Joli (sarcasme)... aucune documentation !

2voto

bob Points 736
$x = 62574 * 62574;

// Cast to an integer
$asInt = intval($x);
var_dump($asInt);
var_dump($asInt % 104659);

// Use use sprintf to convert to integer (%d), which will casts to string
$asIntStr = sprintf('%d', $x);
var_dump($asIntStr);
var_dump($asIntStr % 104659);

6 votes

Que pensez-vous que cela fasse ?

3 votes

-1 ; cette réponse ne comprend pas le problème du PO, et encore moins le résoudre. Appeler intval() sur un flotteur qui est au-dessus PHP_INT_MAX donnera un résultat tout à fait incorrect. La seule raison pour laquelle cela fonctionne pour vous avec 62574 * 62574 (le chiffre utilisé dans la question en 2008) est que sur les versions plus modernes de PHP, ce chiffre est inférieur à celui de l'année précédente. PHP_INT_MAX - mais pour la même raison, le code original de l'auteur de la question fonctionne très bien sur les systèmes modernes et il n'est pas nécessaire de le modifier. Augmentez le nombre à élever au carré jusqu'à ce que vous arriviez à $x être forcé à un flotteur, et vous réaliserez que c'est cassé.

0 votes

Le code montre la différence entre intval et sprintf - offrant un contournement potentiel via sprintf.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X