Page 1 sur 2
erreur de calcul
Posté : 13 déc. 2010, 00:48
par hpmousse
Je suis tombé sur cette erreur de calcul par PHP :
<?php
$a = 0.7;
$b = 7;
$c = $a * $b;
$d = 4.9;
print $d - $c;
?>
et le résultat (qui devrait être 0 pour ceux qui n'auraient pas suivi)
8.881784197E-16
étonnant non ?
Quelqu'un pourrait-il m'expliquer cela ?
Re: erreur de calcul
Posté : 13 déc. 2010, 01:20
par stealth35
Re: erreur de calcul
Posté : 13 déc. 2010, 01:22
par devlop78
Je viens d'essayer ... une bonne colle ! Il ne s'agit apparemment pas d'un problème de typage, ni d'arrondi ...
Pour avoir 0, essaie echo $c - $d - $c + $d; :p
Re: erreur de calcul
Posté : 13 déc. 2010, 01:24
par devlop78
Ceci peut porter à confusion : par exemple, floor((0.1+0.7)*10) retournera 7 au lieu de 8 comme cela pourrait se prévoir, car la représentation interne serait quelque chose comme 7.9.
Je sais pas ce qu'ils fument chez php mais ils faudraient qu'ils arrêtent de jouer à la roulette russe avec les bits ^^
Re: erreur de calcul
Posté : 13 déc. 2010, 01:30
par stealth35
mais c'est bizarre quand même par que si on remplace $c par 4.9 ca marche...
Re: erreur de calcul
Posté : 13 déc. 2010, 01:42
par devlop78
Oui. Et si on fait echo $c, ça affiche bien le truc. Il n'y a qu'une seule décimale,

Re: erreur de calcul
Posté : 13 déc. 2010, 01:43
par devlop78
Par contre 0.5 et 10, ça marche.
Re: erreur de calcul
Posté : 13 déc. 2010, 07:03
par epommate2
0.7 * 7 = 0.4899999999
0.49 - 0.4899999999 = epsilon
Le compte est bon !
Si tu veux savoir si la soustraction de deux flottants donne 0, il faut faire :
$x - $y < $epsilon
mais jamais
$x - $y == 0
Tous est dans la doc :
http://php.net/manual/fr/language.types.float.php
Re: erreur de calcul
Posté : 13 déc. 2010, 21:16
par devlop78
Hmmm ... Je crois avoir compris. Cela est dû à la restriction de la quantité de bits dans la représentation du nombre. Si par exemple, 4,9 s'écrit 10000111100000001 en binaire (c'est du gros pif), mais qu'il est limité en taille, il sera donc arrondi ou coupé, ce qui ne donnera pas 4,9.
* le signe est représenté par un seul bit, le bit de poids fort (celui le plus à gauche)
* l'exposant est codé sur les 8 bits consécutifs au signe
* la mantisse (les bits situés après la virgule) sur les 23 bits restants
Bref, pour une opération arithmétique, mieux vaut utiliser sa calculatrice ^^
Re: erreur de calcul
Posté : 13 déc. 2010, 23:47
par xTG
Il n'y a pas de raison que 4.9 soit coupé. Il y aurait une infinité de chiffres après la virgule je dis pas mais là...
Le codage d'un nombre à virgule revient à mettre tout derrière la virgule (donc la mantisse) et de garder l'exposant pour revenir au nombre original.
Donc 4.9 se codera 0.49 avec un exposant de 1.
La mantisse acceuillera le chiffre 49 (ce qui se code aisément sur 23bits...) et l'exposant prendra un bit sur huit donc...
Question de codage du chiffre il n'y a pas de raison d'avoir de moins bons résultat avec PHP qu'avec la calculatrice, les deux sont codés sur 32bits.
Re: erreur de calcul
Posté : 14 déc. 2010, 00:57
par devlop78
Ce qui me chiffonne aussi c'est que si 4.9 - $x != 0, c'est que $x != 4.9
Or un echo $x nous affiche pourtant bien 4.9. Il faudrait faire le test avec Java ou JavaScript pour voir ce que ça donne. Bientôt 5 - 5 fera 12.
Re: erreur de calcul
Posté : 14 déc. 2010, 07:42
par epommate2
Mais non, c'est comme ça depuis que le monde est monde et le float flottant !
Je me souviens que c'était déjà le cas en C et fatalement, comme PHP est basé dessus, il utilise le même mode de calcul flottant.
Pour faire simple : la mantisse est écrite en base 2 (et pas 10), donc le nombre est écrit :
x0*2^-1 + x1*2^-2 + ...
et comme tous nombre réel (et c'est le cas de 0.49) ne peux s'écrire de cette manière, il est enregistré en 0.4899999999.
(voici la version longue et complexe de ce que j'avance :
http://docs.sun.com/source/806-3568/ncg_goldberg.html )
Re: erreur de calcul
Posté : 14 déc. 2010, 10:40
par xTG
J'ai pas eu le temps de tout lire mais il semblerait que je peux jeter mon cours sur la représentation des nombres flottants en binaire...
L'art de nous apprendre des aspects théoriques qui sont entièrement faux, j'adore...
Le pire dans tout ça c'est que j'ai déjà eu à faire des calcul flottants sur un ARM7TDMI et que j'ai jamais trouvé quoi que ce soit qui bug...
Alors que si ce que cet article avance il n'est pas possible d'obtenir un quelconque calcul correct non ? A moins que dans certains cas il soit capable de combler la marge d'erreur ? Ou bien que la calculette que j'utilisais pour vérifier affichait la même marge d'erreur. x)
Re: erreur de calcul
Posté : 14 déc. 2010, 11:03
par devlop78
Il est clair que l'opération de base tel que le calcul pour un ordinateur, est déroutant à ce niveau là. Si quelqu'un a le temps d'essayer de tester la même chose sur un autre langage de programmation, ce serait avec joie ; On pourrait en conclure, comme le dit l'article sur les nombres floattant php, qu'une addition n'est pas $x + $y, mais doit passer par une fonction qui se charge de cette "marge" telle que SUM($x,$y).
Re: erreur de calcul
Posté : 14 déc. 2010, 11:57
par epommate2
La seule chose qui ne marche pas c'est de tester l'égalité de deux flottants : l'opération devrait être interdite !
echo (0.49 == 0.7*0.7)?"égale":"pas égale";
echo "\n";
echo (bccomp(0.49,bcmul(0.7,0.7))==0)?"égale":"pas égale";
echo "\n";