bug d'arrondi de php

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : bug d'arrondi de php

par Hywan » 24 oct. 2008, 14:50

Oui je connais le codage IEEE 754, quand même (et j'en ai suffisamment bouffer l'année dernière en architecture :P) … J'avais fait le calcul de tête pour voir s'il se terminait ou pas et je mettais arrêté à 2^-6, j'aurais du aller plus loin (mais la fatigue et tout et tout hein …).
Bref, c'est donc normal. QED ;-).

par caroube » 24 oct. 2008, 14:35

J'avoue ne pas comprendre. J'ai re-testé avec 9,99 == (10,0 - 0,01), c'est vrai, mais pas avec cette opération, vraiment bizarre
La notation en virgule flottante représente un nombre décimal sous la forme nombre = M x 2^E (c'est un peu plus compliqué, mais c'est le principe qui importe).
voir http://babbage.cs.qc.edu/IEEE-754/Decimal.html

Sauf que cette représentation entraîne des approximations et que 9.99 est en fait représenté sous la forme une forme hexadécimale qui représente en fait le nombre 9.99000000000000021316 .
$r1 = 9.91;
printf("%01.20f<br>", $r1);
//9.91000000000000014211
$r2 = 19.90;
printf("%01.20f<br>", $r2);
//19.89999999999999857891
$r3 = 9.99;
printf("%01.20f<br>", $r3);
//9.99000000000000021316
$r4 = $r2 - $r1;
printf("%01.20f<br>", $r4);
//9.98999999999999843681
Les opérations arithmétiques ne se font pas à notre manière humaine en mettant les chiffres les uns en dessous des autres, mais avec des algorithmes qui travaillent directement sur les représentations hexadécimales des nombres en virgule flottante. Et ces algos introduisent eux aussi leur lot d'approximation.

Donc, en représentation interne, 19.90 - 9.91 est différent de 9.99. Par contre le programme echo chargé d'afficher un nombre comporte un mécanisme d'arrondi qui fait que 9.99000000000000021316 et 9.98999999999999843681 seront tous les deux affichés sous la forme 9.99.

D'où notre impression que c'est identique alors que ça ne l'est pas.

Ce mécanisme est bien connu de tous ceux qui bossent dans la finance. Si un article à 9.99€ est stocké sous la forme 9.9900000000000002€, on aura un décalage de 2 centimes quand on aura traité un stock de quelques millions de pièces. Et ça, pour un comptable, c'est inacceptable.
C'est pour ça que dans certains langages, on a la notion de nombre décimal différente de nombre à virgule flottante. Les nombres décimaux sont stockés avec leur valeur exacte (9.99) quasiment sous forme de chaîne de caractères et les opérations arithémétiques se font grosso-modo à la manière humaine.

par Shrell » 24 oct. 2008, 10:29

ups, désolé pour la parenthèse :oops:
merci pour la lien savageman, je teste ça de suite ;)
EDIT :
Euh non en fait il n'y avait pas de problème de parenthèse, ça marchait bien

par savageman » 24 oct. 2008, 06:36

http://fr.php.net/manual/fr/language.ty ... uote]Ainsi, ne faite jamais confiance aux derniers chiffres d'un nombre décimal, mais aussi, ne comparez jamais l'égalité de 2 nombres décimaux. Si vous avez besoin d'une haute précision, les fonctions mathématiques de précision et les fonctions gmp sont disponibles.[/quote]

par Hywan » 24 oct. 2008, 00:35

Hey :),

La première : 9,99 est égal à 9,99, normal.
La seconde : 9,99 est égal à 10 - 0,01, soit 9,99, normal (sachant que 10 est équivalent à 10,0 en PHP).
La troisième: 9,99 est égal à 9 + 0,99, normal (raisonnement analogue à précédemment).
La quatrième : 9,99 ne vaut pas 19,90 - 9,91, soit 9,99 … Bizarre :-k, voici mes tests :

Code : Tout sélectionner

$ php -a Interactive mode enabled <?php var_dump(9.99 == (19.90 - 9.91)); bool(false) var_dump(19.90 - 9.91); float(9.99) var_dump(9.99); float(9.99) $a = 9.99; $b = 19.90 - 9.91; var_dump($a == $b); bool(false) var_dump($a, $b); float(9.99) float(9.99)
J'avoue ne pas comprendre. J'ai re-testé avec 9,99 == (10,0 - 0,01), c'est vrai, mais pas avec cette opération, vraiment bizarre :-k
Le dernier exemple (selon arthur) est correct, normal.

Donc, seul l'exemple trois est incorrect et je ne me l'explique. Intéressant. Quelqu'un a une idée ?

Au passage : testé avec PHP 5.2.6.

par @rthur » 24 oct. 2008, 00:19

Pour ton dernier exemple, il s'agit d'une erreur de ta part de parenthèses :
<!-- encore plus fort !!! -->
<?php echo var_dump(9.99 != round((19.90 - 9.91), 2)); ?><br />

par Shrell » 23 oct. 2008, 21:04

Bon, la même pour les intégristes :lol:
<?php echo var_dump(9.99 != 9.99); ?><br />
<?php echo var_dump(9.99 != 10 - 0.01); ?><br />
<?php echo var_dump(9.99 != 9 + 0.99); ?><br />
<?php echo var_dump(9.99 != 19.90 - 9.91); ?><br />
<!-- encore plus fort !!! -->
<?php echo var_dump(9.99 != round(19.90 - 9.91, 2)); ?><br />
mais ça change rien au biniou ;)

par Victor BRITO » 23 oct. 2008, 20:59

Et si tu balisais ton code PHP en évitant la notation courte ? ;) Au lieu de <?=, écris <?php echo.

bug d'arrondi de php

par Shrell » 23 oct. 2008, 20:13

Hello
Je crois que je viens de trouver un bug de php :D
(j'ai l'air content comment ça mais j'ai passé l'après midi à me demander ce qui se passait ...)
donc, venons en au fait :
ma version de php : 5.1.6
recette : prenez une page vierge, et faite ça :
<?=var_dump(9.99 != 9.99); ?><br />
<?=var_dump(9.99 != 10 - 0.01); ?><br />
<?=var_dump(9.99 != 9 + 0.99); ?><br />
<?=var_dump(9.99 != 19.90 - 9.91); ?><br />
<!-- encore plus fort !!! -->
<?=var_dump(9.99 != round(19.90 - 9.91, 2)); ?><br />
etonnant non? :lol: