Php qui fait des calculs faux
julienvo
Messages postés
5
Date d'inscription
Statut
Membre
Dernière intervention
-
yg_be Messages postés 23541 Date d'inscription Statut Contributeur Dernière intervention -
yg_be Messages postés 23541 Date d'inscription Statut Contributeur Dernière intervention -
Bonjour,
dans mon script php j'ai besoin à un moment de faire un calcul, je précise que sur le même script je fait des centaine de calcule avec cette même formule, ils sont tous juste, mais pour un seule d'entre eux, php me renvoi un résultat faux :
688.16 - 275 - 413.16 = -5.6843418860808E-14
au lieu de 0
mon code :
Comprenez-vous l'erreur?
voici les valeur assigné à ses entrée
$TOTAL = 688.16;
$montant_premier_payement = 275;
$montant_second_payement = 413.16;
$montant_troisieme_payement = 0;
Je pense peut être chercher du coté de l'encodage dans la BDD, mais je comprend pas pourquoi parmi les 800 enregistrement que j'ai dans ma BDD, seulement sur celle-ci j'ai un problème.
Merci pour votre retour
dans mon script php j'ai besoin à un moment de faire un calcul, je précise que sur le même script je fait des centaine de calcule avec cette même formule, ils sont tous juste, mais pour un seule d'entre eux, php me renvoi un résultat faux :
688.16 - 275 - 413.16 = -5.6843418860808E-14
au lieu de 0
mon code :
<?php
$rek = mysql_query("SELECT * FROM facture where id_membre = '$_ID' ");
while($donnees = mysql_fetch_array($rek))
{
$montant_premier_payement = $donnees['montant_premier_payement'];
$montant_second_payement = $donnees['montant_second_payement'];
$montant_troisieme_payement = $donnees['montant_troisieme_payement'];
$TOTAL = $donnees['TOTAL'];
$solde_restant = $TOTAL-$montant_premier_payement-$montant_second_payement-$montant_troisieme_payement;
?>
Comprenez-vous l'erreur?
voici les valeur assigné à ses entrée
$TOTAL = 688.16;
$montant_premier_payement = 275;
$montant_second_payement = 413.16;
$montant_troisieme_payement = 0;
Je pense peut être chercher du coté de l'encodage dans la BDD, mais je comprend pas pourquoi parmi les 800 enregistrement que j'ai dans ma BDD, seulement sur celle-ci j'ai un problème.
Merci pour votre retour
Configuration: Linux / Firefox 78.0
A voir également:
- Logiciels lui permettant de faire des calculs sur des tableaux de nombres
- Logiciel de sauvegarde gratuit - Guide
- Roland souhaite calculer le montant total des ventes de son magasin. le fichier contient, pour chaque produit, la quantité vendue et le prix unitaire. calculez le montant total des ventes. ✓ - Forum Excel
- Tableau des codes ascii - Guide
- Un des logiciels lui permettant de faire des calculs sur des tableaux de nombres - Forum C#
- Comment calculer la moyenne sur excel - Guide
7 réponses
Bonjour,
Déjà, force les valeurs en FLOAT
https://www.php.net/manual/fr/function.floatval.php
Et fais un var_dump de ta variable $donnees pour voir exactement ce qu'elle contient. (et montres le nous)
Déjà, force les valeurs en FLOAT
https://www.php.net/manual/fr/function.floatval.php
Et fais un var_dump de ta variable $donnees pour voir exactement ce qu'elle contient. (et montres le nous)
Bonjour à tous les 2
@julienvo, tu peux aller faire un tour sur cette discussion
https://forums.commentcamarche.net/forum/affich-35846831-erreur-de-calcul#3
J'ai commencé par une vulgarisation que Dalfab a complétée. Reivax lui a parlé d'une solution propre à Python, je ne sais pas s'il y a un équivalent en PHP.
@julienvo, tu peux aller faire un tour sur cette discussion
https://forums.commentcamarche.net/forum/affich-35846831-erreur-de-calcul#3
J'ai commencé par une vulgarisation que Dalfab a complétée. Reivax lui a parlé d'une solution propre à Python, je ne sais pas s'il y a un équivalent en PHP.
Oui je confirme, c'est lié à la précision de la décimal...
Une solution pour contourner le souci ( à conditions que les nombres n'aient, au maximum, que deux chiffres après la virgules, est de les multiplier par 100 puis de diviser la somme par 100 )
par exemple
Une solution pour contourner le souci ( à conditions que les nombres n'aient, au maximum, que deux chiffres après la virgules, est de les multiplier par 100 puis de diviser la somme par 100 )
par exemple
$a = 688.16 *100; $b = 275 *100; $c = 413.16 *100; echo ($a - $b - $c)/100;
Bonjour,
merci pour votre aide.
pour répondre dans l'ordre, voici ce que retourne var_dump();
Si je change ma bdd en float, c'est pareil, même résultat
Ensuite, j'ai essayer la technique de multiplier puis diviser par 100, je comprend pas vraiment le but, mais ça change rien
Sinon j'ai entre temps modifier les formatage de la bdd et j'ai fait des essai, je pense que le problème vient de la.
j'ai mis les valeur de la table sur FLOAT 10,0 et le problème à disparu, je l'ai remis à 10,2 et il n'ai pas réapparu, car les chiffres après la virgule on disparu, donc le problème peut être résolu en cherchant par la?
merci pour votre aide.
pour répondre dans l'ordre, voici ce que retourne var_dump();
var_dump($TOTAL);
var_dump($montant_premier_payement);
var_dump($montant_second_payement);
var_dump($montant_troisieme_payement);
string(6) "688.16"
string(6) "275.00"
string(6) "413.16"
string(4) "0.00"
Si je change ma bdd en float, c'est pareil, même résultat
Ensuite, j'ai essayer la technique de multiplier puis diviser par 100, je comprend pas vraiment le but, mais ça change rien
$TOTAL = $donnees['TOTAL']*100/100;
$montant_premier_payement = $donnees['montant_premier_payement']*100/100;
$montant_second_payement = $donnees['montant_second_payement']*100/100;
$montant_troisieme_payement = $donnees['montant_troisieme_payement']*100/100;
Sinon j'ai entre temps modifier les formatage de la bdd et j'ai fait des essai, je pense que le problème vient de la.
j'ai mis les valeur de la table sur FLOAT 10,0 et le problème à disparu, je l'ai remis à 10,2 et il n'ai pas réapparu, car les chiffres après la virgule on disparu, donc le problème peut être résolu en cherchant par la?
ok, mais je comprend quand même pas le but, ça fait un peut bricolage et j’ai peur de m’embrouiller, en enregistrant une valeur qui à été multiplié, la page de code est très grande, et j'utilise cette base sur plusieurs page.
Si il y à une autre solution, et je suis sur que oui, je suis preneur.
Le problème c'est que les valeurs sont affiché en string et non en number, il y à pas une fonction qui force l'enregistrement en number?
J'utilise des . et pas des virgule pour l'enregistrement, c'est peut-être ça?
Si il y à une autre solution, et je suis sur que oui, je suis preneur.
Le problème c'est que les valeurs sont affiché en string et non en number, il y à pas une fonction qui force l'enregistrement en number?
J'utilise des . et pas des virgule pour l'enregistrement, c'est peut-être ça?
ok, je comprend mieux , donc le problème c'est qu'il y avait trop de chiffre après la virgule d'après toi?
Ce que je comprend pas, c'est qu'ils été enregistré dans ma bdd en DECIMAL(10,2) ce qui veut dire un nombre entier de 10 chiffres maximum, avec une virgule et puis deux chiffres, donc si ce que tu dit est vrais, ça veut dire pa rexemple, que 688.16 en fait c'est ce qui est affiché, mais ce qui est enregistré c'est 688.16526523525626525225552 ou un truc comme ça.
Ce que je comprend pas, c'est qu'ils été enregistré dans ma bdd en DECIMAL(10,2) ce qui veut dire un nombre entier de 10 chiffres maximum, avec une virgule et puis deux chiffres, donc si ce que tu dit est vrais, ça veut dire pa rexemple, que 688.16 en fait c'est ce qui est affiché, mais ce qui est enregistré c'est 688.16526523525626525225552 ou un truc comme ça.
dans ta base de données, c'est bien enregistré comme 688.16.
par contre, PHP va peut-être le transformer plutôt en 688.16000000000001 ou 688.159999999999999.
en effet, PHP ne connait pas ce concept DECIMAL(10,2).
comme tu sais que ce sont des nombres décimaux à deux chiffres après la virgule, le plus prudent, c'est de multiplier ces nombres par 100, et que PHP les traite comme des entiers.
cela élimine les problèmes d'arrondi.
par contre, PHP va peut-être le transformer plutôt en 688.16000000000001 ou 688.159999999999999.
en effet, PHP ne connait pas ce concept DECIMAL(10,2).
comme tu sais que ce sont des nombres décimaux à deux chiffres après la virgule, le plus prudent, c'est de multiplier ces nombres par 100, et que PHP les traite comme des entiers.
cela élimine les problèmes d'arrondi.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
yg_be
Messages postés
23541
Date d'inscription
Statut
Contributeur
Dernière intervention
Ambassadeur
1 584
bonjour,
je ferais:
je ferais:
$solde_restant = round($TOTAL-$montant_premier_payement-$montant_second_payement-$montant_troisieme_payement , 2);
Merci pour ton idée, comme je l'ai dit avant, j'utilise ses donnée dans plusieurs page, je pense que la piste est bonne, mais qu'il fraudais enregistrer ça dans la bdd par exempleou bien de sortire les donnée de la bdd comme ca
$TOTAL = $donnees['TOTAL']; round($TOTAL, 2);
$TOTAL = $donnees['TOTAL']; round($TOTAL, 2);
Bonjour
je pense que tu n'as pas compris le fond du problème.
Dans ton calcul, simple au demeurant, il y a au moins un nombre que l'ordinateur ne peut pas représenter. Que ce soit un des nombres d'entrée, un résultat intermédiaire ou le résultat final, peu importe.
Du coup ce nombre est "arrondi" au nombre le plus proche que l'ordinateur peut représenter. A 5E-14 près c'est pas la mort quand même.
Donc arrondir le résultat à la fin est un artifice. Stocker des arrondis dans ta base de données va à l'encontre de la précision des tes futurs calculs car tu ajoutes un biais à un résultat déjà biaisé.
La meilleure solution (informatiquement parlant) est celle proposée par Jordane.
Supposons que tes données soient de l'argent.
Si tu calcules tout en centimes (en multipliant par 100 les entrées) , ça devient des nombres entiers. Et contrairement au nombres à virgules, les nombres entiers sont tous représentables pour l'ordinateur (dans la limites du types, par exemple en C# un entier simple va de -2 147 483 648 à +2 147 483 647)
Donc jusqu'à la dernière étape ton calcul est 100% juste. Et la division par 100 d'un nombre entier (pour repasser en euros), normalement ne pose pas de problème d'imprécision.
je pense que tu n'as pas compris le fond du problème.
Dans ton calcul, simple au demeurant, il y a au moins un nombre que l'ordinateur ne peut pas représenter. Que ce soit un des nombres d'entrée, un résultat intermédiaire ou le résultat final, peu importe.
Du coup ce nombre est "arrondi" au nombre le plus proche que l'ordinateur peut représenter. A 5E-14 près c'est pas la mort quand même.
Donc arrondir le résultat à la fin est un artifice. Stocker des arrondis dans ta base de données va à l'encontre de la précision des tes futurs calculs car tu ajoutes un biais à un résultat déjà biaisé.
La meilleure solution (informatiquement parlant) est celle proposée par Jordane.
Supposons que tes données soient de l'argent.
Si tu calcules tout en centimes (en multipliant par 100 les entrées) , ça devient des nombres entiers. Et contrairement au nombres à virgules, les nombres entiers sont tous représentables pour l'ordinateur (dans la limites du types, par exemple en C# un entier simple va de -2 147 483 648 à +2 147 483 647)
Donc jusqu'à la dernière étape ton calcul est 100% juste. Et la division par 100 d'un nombre entier (pour repasser en euros), normalement ne pose pas de problème d'imprécision.