Remi Posté 1 Mars 2009 Posté 1 Mars 2009 Bonjour, Quelqu'un peut-il m'expliquer ce phénomène étrange qui m'a fait faire des heures supplémentaires : Avec : $var=0.71*15*100; je devrais me retrouver avec une variable de type double qui contient 1065. Et bien si je fais : if ($var == 1065) print "OK"; ça ne marche pas....! Explication : print $var." ".intval($var); donne 1065 1064 Quand on passe en integer simple, on a 1064 !! En fait 0.71*15*100 semble donner 1064,99999 Ce n'est pas un peu bizarre....? Remi
Sarc Posté 1 Mars 2009 Posté 1 Mars 2009 En effet, bizarre... Ceci-dit, PHP n'est pas un langage de calcul mathématique, va savoir comment il les fait ! Au lieu d'utiliser intval, je pense qu'il serait mieux d'utiliser "round"... Comme ça, même s'il te met des ,9999 ou des ,0001 il te sortira la bonne valeur. Mais si quelqu'un sait comment PHP gère les multiplications au niveau assembleur, ça m'intéresse, parce que j'avoue ne pas voir comment arriver à un résultat pareil. :|
Jeanluc Posté 1 Mars 2009 Posté 1 Mars 2009 En fait 0.71*15*100 semble donner 1064,99999 Ce n'est pas un peu bizarre....? Oui et non. De nombreux nombres décimaux ne sont pas représentés exactement par PHP. C'est évident pour 0,333333333...333... (= 1/3), mais c'est aussi le cas de nombres qui paraissent faciles à représenter en binaire. A partir du moment où le calcul n'est pas fait exclusivement avec des entiers, on ne devrait plus tester qu'on a une égalité, mais seulement que la différence est négligeable pour l'application: $variable1 - $variable2 < $EPSILON où $EPSILON est égal à 0.0000001 (par exemple). Jean-Luc
jcaron Posté 2 Mars 2009 Posté 2 Mars 2009 (modifié) Pour compléter (et je viens de le comprendre en cherchant un peu): n'importe quel nombre décimal qui ne peut pas s'écrire sous la forme d'un entier (de taille raisonable) en le multipliant par 2^n (presque tous, donc) ne peut pas être représenté de façon exacte en flottant. C'est le cas de 0.71 (en binaire 0.101101011100001010001111010111000010100011110101110000101000 et plein d'autres chiffres, on voit bien la répétition). Il y a donc un écart et on ne fait qu'ajouter de l'imprécision quand on fait d'autres opérations, et donc au final tu as un écart (de l'ordre de 10^-13 dans ton calcul). Au passage, pour faire la comparaison, il faut évidemment penser à mettre un coup de valeur absolue sur la différence! :-) Jacques. Modifié 2 Mars 2009 par jcaron
Remi Posté 2 Mars 2009 Auteur Posté 2 Mars 2009 Merci pour vos réponses. Oui, je me suis aperçu que c'est effectivement courant. Avec un for ($i=1,$cpt=0;$i<1000;$i++) { $var= 0.2 * $i * 10; if ($var != intval($var)) { print "$i "; $cpt++; }}print "soit $cpt cas "; on voit qu'il y a un problème dans 25% des cas! (en revanche, 0.2*10*$i est toujours OK donc si la première opération donne un nombre entier, on reste en variable double mais l'imprécision n'est plus là) Merci pour l'idée de transformer les (test $var1 == $var2) par (abs($var1-$var2) < 0.001). L'autre solution est de mettre des round, mais on risque d'en oublier... Je suis tout de même très étonné par tout ça. Je pensais que les imprécisions des variables en type double étaient limitées à des nombres complexes. C'est tout de même étonnant qu'il ait du mal avec par exemple 0.2 * 3
jcaron Posté 2 Mars 2009 Posté 2 Mars 2009 Mon conseil personnel: si tu as besoin d'un résultat précis (par exemple si tu manipules des montants en euros avec des centimes), toujours travailler en virgule fixe (i.e. avec des entiers qui représentent des centimes). Ca t'évitera énormément de soucis... Jacques.
destroyedlolo Posté 2 Mars 2009 Posté 2 Mars 2009 Cette discussion me laisse vraiment pantois : c'est un probleme qui est connu depuis que les calculs binaires existent PHP n'a strictement rien a faire la dedans : ce sont les librairies mathematiques du systemes qui etablissent la precision de ces calculs. Les solutions sont : Augmenter les precisions internes (genre calculer sur 10 digits pour n'en afficher que 8). utiliser du BCD plutot que du binaire pure utiliser uniquement du calcul formel lorsque c'est possible. Mais faut etre clair, hormis le calcul formelle, y'a rien de miraculeux, hein ...
Sujets conseillés
Veuillez vous connecter pour commenter
Vous pourrez laisser un commentaire après vous êtes connecté.
Connectez-vous maintenant