Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

php se plante ? 1.0+1.0 != 2.0

4 réponses
Avatar
FAb
Bonsoir,
En debuggant une appli php je tombe sur un jeu de valeur qui fait une erreur
toute bête de calcul. Soit l'extrait suiant :

===
$montant_ht = 263.29;
$tva = 51.61;
$montant_ttc = 314.90;

$sum = $montant_ht + $tva;

print("[sum=$sum] \n");
if($sum == 314.90) {
print("sum egalite float \n");
} else {
print("pas egal\n");
}
die("fin\n");
===

La sortie en est :
===
[sum=314.9]
pas egal
fin
===

En effet je constate un delta entre $sum et la vraie valeur $montant_ttc :
float(-5.6843418860808E-14)

Donc mes questions sont :
- Comment détecter ce genre de saleté ?
- Comment contourner le problème le cas échéant ? (On manipule des sous donc le
bricolage me laisserait dubitatif).

Quand au pourquoi si certains savent de tête je suis preneur aussi. Car mes
souvenirs sur les erreurs de floats ne me rappelle rien sur de telles valeurs
banales. Je regarderai ce soir chez moi dans mes grimoires...

Merci,
FAb

4 réponses

Avatar
motton75
C'est classique et on ne le repetera jamais assez : les nombres
decimaux ne sont pas stockables en machine tels quels - ils sont
arrondis a la valeur la plus proche effectivement stockable en machine.
C'est ce que l'on appelle le bruit numerique.

Par consequent, tester dans n'importe quel contexte l'egalite stricte
de deux float (ou l'inegalite stricte) est une erreur de programmation
: c'est trop instable. A l'exception, a la limite, de la comparaison
avec zero ou avec un float entier.

La bonne demarche est d'ecrire, au lieu de :
if (a == 0.01) // instable
la ligne suivante :
if (fabs(a-0.01) < tol)
Ou tol est une tolerance, par exemple 1.e-3 si on compare des nombres
dont seuls les deux premiers chiffres apres la virgule nous
interessent.
Avatar
Thomas Harding
Le 02-08-2006, FAb a écrit :
En effet je constate un delta entre $sum et la vraie valeur $montant_ttc :
float(-5.6843418860808E-14)

Donc mes questions sont :
- Comment détecter ce genre de saleté ?
- Comment contourner le problème le cas échéant ? (On manipule des sous donc le
bricolage me laisserait dubitatif).


Il faut voir du côté de GMP, qui permet de manipuler des nombres de
précision arbitraire.

Sinon, il existe une fonction "round" ou "rnd", qui permet de fixer
l'arrondi, (voir aussi éventuellement sprintf, mais j'éviterais cette
solution pour autre chose que l'affichage).

--
Thomas Harding

Avatar
FAb
"motton75" writes:

C'est classique et on ne le repetera jamais assez : les nombres
decimaux ne sont pas stockables en machine tels quels - ils sont
arrondis a la valeur la plus proche effectivement stockable en machine.
C'est ce que l'on appelle le bruit numerique.


Oui. Merci IEEE754. J'ai fini par tester en SQL et en C (float et double).

En fait ces nombres auraient du être stockés en numeric dans la base où je les
lisais puis manipulés avec des types de précision arbitraire bc* en php.


Par consequent, tester dans n'importe quel contexte l'egalite stricte
de deux float (ou l'inegalite stricte) est une erreur de programmation
: c'est trop instable. A l'exception, a la limite, de la comparaison
avec zero ou avec un float entier.

La bonne demarche est d'ecrire, au lieu de :
if (a == 0.01) // instable
la ligne suivante :
if (fabs(a-0.01) < tol)
Ou tol est une tolerance, par exemple 1.e-3 si on compare des nombres
dont seuls les deux premiers chiffres apres la virgule nous
interessent.


Oui on peut introduire un epsilon de tolérance... Mais dans mon cas cela me
demande de revoir certaines requêtes. Je pense pencher pour convertir en type
plus précis mais plus coûteux.

Merci,
FAb

Avatar
motton75
Oui on peut introduire un epsilon de tolérance... Mais dans mon cas cela me
demande de revoir certaines requêtes. Je pense pencher pour convertir en type
plus précis mais plus coûteux.


Sinon, si tu n'as pas besoin de trop de chiffres, utilise un type
entier (genre BIGINT pour les grosses sommes ?) - tu stockes tous les
montants en centimes ou en milliemes d'euros et tu ne rajoutes la
virgule qu'au moment de l'affichage ?