Facebook LinkedIn SourceForge Twitter RSS LastFM
logologo

La comparaison de nombres à virgule flottante en PHP

Geoffray Warnants|30/07/2010|6 commentaires

Malgr les considrables volutions qu'a subi PHP, on ne peut nier qu'il est encore aujourd'hui trahi par de petites imperfections qui salissent son blason. Les dtracteurs s'en donnent coeur joie pour critiquer l'amateurisme du langage, les autres, certainement un peu fatalistes, prfrent dire qu'il a le mrite d'tre une source intarissable de surprises ;)

Dernire curiosit en date : La comparaison de 2 nombres dcimaux n'est pas fiable. Ainsi :

var_dump(0.2+0.3 == 0.5);   // affiche true
var_dump(0.2+0.4 == 0.6);   // affiche false

Le second rsultat est pour le moins inattendu ! Il se comprend nanmoins en regardant de plus prs la valeur relle de chacune des valeurs jusqu' leurs infimes dcimales.

printf('%.20f', 0.2);       // 0.20000000000000001110
printf('%.20f', 0.3);       // 0.29999999999999998890
printf('%.20f', 0.2+0.3);   // 0.50000000000000000000
printf('%.20f', 0.5);       // 0.50000000000000000000

printf('%.20f', 0.2);       // 0.20000000000000001110
printf('%.20f', 0.4);       // 0.40000000000000002220
printf('%.20f', 0.2+0.4);   // 0.60000000000000008882
printf('%.20f', 0.6);       // 0.59999999999999997780

"Honteux" crierons les dtracteurs, "normal" diront simplement les autres. En effet, un avertissement dans la doc PHP nous met en garde face ce comportement hasardeux bien connu et l'explique par le fait que la reprsentation interne de certains nombres dcimaux n'est pas possible sans une infime perte de prcision. Une remarque qui n'est pas sans raviver mes lointaines notions d'ASM et les prises de tte pour comprendre la reprsentation des dcimaux en binaire.

Bref, ce problme d'approximation est inhrent nos ordinateurs. On le rencontre d'ailleurs dans d'autres langages de bas niveau, notamment en C, langage avec lequel est crit PHP.

Que faut-il faire ?

Comme indiqu dans la doc, comparer des nombres dcimaux de manire classique est proscrire. Pour ce genre d'opration qui ncessite une prcision importante, PHP fourni un ensemble de fonctions spcifiques, dont bccomp qui permet de comparer des nombres de grande taille.

var_dump(bccomp(0.2+0.4, 0.6)==0); // affiche true

Une autre solution plus artisanale consiste caster les nombres en chanes de caractres avant d'effectuer la comparaison.

var_dump((string)(0.2+0.4)==(string)0.6); // affiche true

Cette solution semble tout aussi acceptable puisque lors de la conversion, PHP prend soin de retirer les ventuels zros non significatifs qui pourraient poser problme en cas de comparaison textuelle.

var_dump((string)(0.2+0.4)==(string)000.60000); // affiche true aussi

La suite n'est plus qu'une histoire de prfrence...

<<< Retour

Vos commentaires

4 commentaires posts

Lucie Collin
04/01/2021 07:53Post par Lucie Collin
Le choix des lettres et chiffres utilis? dans le langage de programmation ne se fait pas ? la l?g?re. De ce fait, on doit tol?rer toutes les critiques, car c?est notre m?tier lorsqu?on publie quelques langages.
Geoffray
04/08/2010 20:50Post par Geoffray
Effectivement, le terme n'est pas des plus judicieux... Voilà ce qui arrive quand on recopie la doc comme un idiot ;)
Syndrael
04/08/2010 11:28Post par Syndrael
"dont bccomp qui permet de comparer des nombres de grande taille."..quand on voit l'exemple ça laisse perplexe.. LOL !!
S.
OuT
31/07/2010 15:48Post par OuT
j'ai aussi cet exemple, qui est assez traître ;)

$arrondi = round(1.4);
if ($arrondi == 1) {
// OK
}
if ($arrondi === 1) {
// comparaison stricte FAIL
}

var_dump($arrondi);

et oui, malgré le fait que round(), floor() et ceil() renvoient forcément un nombre entier, le résultat est de type "float" ;)

Ragir cet article

*


(Ne sera pas publie, servira uniquement afficher votre gravatar)


(Lien en dur et dofollow)

zend framework