Php qui fait des calculs faux

Fermé
julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021 - 15 oct. 2021 à 18:15
yg_be Messages postés 22708 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 20 avril 2024 - 18 oct. 2021 à 08:21
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 :
<?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

7 réponses

jordane45 Messages postés 38139 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 20 avril 2024 4 649
15 oct. 2021 à 18:34
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)
0
Whismeril Messages postés 19025 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 19 avril 2024 929
16 oct. 2021 à 06:31
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.
0
jordane45 Messages postés 38139 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 20 avril 2024 4 649
16 oct. 2021 à 10:35
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
$a = 688.16 *100;
$b = 275 *100;
$c = 413.16 *100;


echo ($a - $b - $c)/100;
0
julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021
16 oct. 2021 à 19:21
Bonjour,
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?
0
jordane45 Messages postés 38139 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 20 avril 2024 4 649
16 oct. 2021 à 20:12
par contre.. il ne faut pas diviser chaque chiffre ... mais le calcul total
0
julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021
17 oct. 2021 à 09:45
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?
0
jordane45 Messages postés 38139 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 20 avril 2024 4 649
17 oct. 2021 à 09:56
Si tes nombres ont (au maximum) deux chiffres après la virgule, en les multipliant par 100 ça permet de virtuellement les retirer.
Ensuite, tu divises par 100 le total pour remettre la décimal.... c'est des maths .. rien de plus..
0
julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021 > jordane45 Messages postés 38139 Date d'inscription mercredi 22 octobre 2003 Statut Modérateur Dernière intervention 20 avril 2024
17 oct. 2021 à 14:02
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.
0
yg_be Messages postés 22708 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 20 avril 2024 1 474 > julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021
18 oct. 2021 à 08:21
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.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
yg_be Messages postés 22708 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 20 avril 2024 1 474
17 oct. 2021 à 11:05
bonjour,
je ferais:
$solde_restant 	   = round($TOTAL-$montant_premier_payement-$montant_second_payement-$montant_troisieme_payement , 2);
0
julienvo Messages postés 5 Date d'inscription jeudi 17 décembre 2020 Statut Membre Dernière intervention 17 octobre 2021
17 oct. 2021 à 14:13
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);
0
Whismeril Messages postés 19025 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 19 avril 2024 929
18 oct. 2021 à 06:28
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.
0