calcul faux [résolu] - Java - Programmation
Marsh Posté le 18-09-2006 à 11:09:40
les nombres décimaux sont rarement stockés sous leur forme exact , mais sous une valeur approchée ( d'assez pres )
Marsh Posté le 18-09-2006 à 11:16:57
OK donc la dessus je fais un arrondi à deux décimales près pour obtenir mon montant exact. (C'est la solution temporaire que j'avais utilisé mais je voulais avoir une explication !)
C'est la meilleure solution ?
Merci en tout cas pour ta réponse super rapide.
Marsh Posté le 18-09-2006 à 11:18:04
utilises des doubles au lieu des floats et tu auras le resultat que tu veux
Marsh Posté le 18-09-2006 à 11:19:55
oui car les nombres pseudo-réels sont stockés en binaire, par puissance de 2 successives.
or, 0.1 ne peut pas être stocké entièrement par puissance de 2 (0.0625+0.03125 etc)
Marsh Posté le 18-09-2006 à 11:20:24
gocho a écrit : utilises des doubles au lieu des floats et tu auras le resultat que tu veux |
ça ne change rien au problème, ça le repousse
Marsh Posté le 18-09-2006 à 11:27:22
jagstang a écrit : ça ne change rien au problème, ça le repousse |
Que faire dans un tel cas alors ?
stocker les valeurs * 2 ?
Marsh Posté le 18-09-2006 à 11:28:01
Je savais que Java n'était pas le meilleur langage pour faire des calcul mais quand même là
Code :
|
ça me donne 23.1 23.099998 23.1
Netbean me prévient d'une possible perte de précision avec "montant" en float. En prennant des double le résultat semble meilleur mais c'est pê reculer pour mieux sauter, que ce passe-t-il avec un "gros" double ?
Marsh Posté le 18-09-2006 à 11:29:37
moustik510 a écrit : Que faire dans un tel cas alors ? |
je vois pas ce que ça change
Marsh Posté le 18-09-2006 à 11:29:59
une solution con aussi : calculer en centimes (avec des entiers donc)
Marsh Posté le 18-09-2006 à 11:40:25
dès que tu fais du monétaire il __faut__ travailler avec des types entiers (j'ai pas dit int, j'ai dit type entier, arithmétique entière). Si t'as un flottant dans le tas, c'est foutu.
Marsh Posté le 18-09-2006 à 11:51:36
Ricco a écrit : Je savais que Java n'était pas le meilleur langage pour faire des calcul mais quand même là |
le problème est le même dans tous les langages
regarde sur cette page : http://www.arcanapercipio.com/gen/binaire/bin-nb.php la partie http://www.arcanapercipio.com/gen/binaire/bin-nb.php
jagstang a écrit : une solution con aussi : calculer en centimes (avec des entiers donc) |
Taz a écrit : dès que tu fais du monétaire il __faut__ travailler avec des types entiers (j'ai pas dit int, j'ai dit type entier, arithmétique entière). Si t'as un flottant dans le tas, c'est foutu. |
+1
si tu dois aller au millieme de centime pret , tu multiplie toutes nombres par 100 000
lors de l'affichage tu fais la manip inverse
Marsh Posté le 18-09-2006 à 11:58:53
Ricco a écrit : Je savais que Java n'était pas le meilleur langage pour faire des calcul mais quand même là |
Ce problème n'a strictement aucun rapport avec Java et n'est en rien spécifique à l'utilisation de Java, il trouve son origine dans la représentation binaire des nombres flottants (norme IEEE754): certains nombres flottants décimaux exacts ne peuvent être encodés de manière exacte en binaire. Tous les langages utilisant un type réel "floating-point" sont affectés. Certains langages disposent par ailleurs d'un type réel "fixed-point" dont la précision est exacte et fixée sur une range donnée, et d'un type "decimal" à la précision infinie (mais notablement moins efficace, et prenant largement plus de place)
De même que certaines fractions ne peuvent être encodées de manière exacte en décimal (1/3 ~ 0.333~)
Merci, donc, d'arrêter de raconder des conneries, et de lire What Every Computer Scientist Should Know About Floating-Point Arithmetic
Marsh Posté le 18-09-2006 à 12:00:44
flo850 a écrit : +1 |
Ah ... ça me dis quelque chose ça. on a du me le dire pendant les cours... mais j'ai pas du le noter. Et je n'utilise pas Java professionnellement (dev SAP) donc j'évolue pas.
Je vais faire de la multiplication par 100. Merci pour vos réponse. Comment passer le sujet en résolut ?
Marsh Posté le 18-09-2006 à 12:01:59
edit ton premier post
Marsh Posté le 18-09-2006 à 15:11:25
Taz a écrit : BigDecimal |
C'est pour toi la meilleure solution ?
(Sachant que je ne travaillerai pas avoir des nombres très grands mais pour être plus propre ...)
Marsh Posté le 18-09-2006 à 15:22:11
Decimal tout court doit faire l'affaire logiquement. A moins qu'e Java Decimal soit un alias de Float, il utilise une précision fixe, et donc ne pose pas ces problèmes d'approximation (si ?)
Mais sinon, *100 et dans un Int ça reste de loin la meilleure solution.
Marsh Posté le 18-09-2006 à 15:43:25
MagicBuzz a écrit : Decimal tout court doit faire l'affaire logiquement. A moins qu'e Java Decimal soit un alias de Float, il utilise une précision fixe, et donc ne pose pas ces problèmes d'approximation (si ?) |
Si. Il suffit de faire 3 * 0.7. Ce qui est donc complètement pourri. En compta t'es pas la pour jouer à madame irma. le Calcul doit être exact du début jusqu'à la fin. Si tu fais une somme de float, l'imprécision se propage et grandit linéairement. Et là tu plantes ta compta.
BigDecimal c'est très bien, et t'auras une bonne propagation d'échelle que tu pourras gérer toi même. Par exemple du fais un calcul avec la précision maximale genre 175.25 * 0.03 ça va te faire du 5.2575 et quand tu enregistres tes données, tu peux tronquer. Comme ça c'est parfait, tu as fait le calcul exact tout le long.
Marsh Posté le 18-09-2006 à 15:55:50
Ma question était : est-ce que Decimal en Java est comme le Decimal en SQL, c'est à dire TOUT SAUF UN FLOAT. (en réalité, une chaîne de caractères d'un nombre fixe de chiffres).
Ce type (en SQL tout du moins) permet de stocker des valeurs à virgule d'une façon très précise, sans le moindre risque d'erreur d'arrondi.
Et Decimal tout court devrait suffir amplement. BigDecimal, comme tout "big", n'est qu'une solution pourrie pour repousser le problème d'arrondi, sans s'en protéger.
Dans tous les cas, une application COMPTA ne compte qu'en entiers, ou en types à précision fixe (Decimal en SQL donc). Jamais de la vie en float, même si c'est un float sur 64 bits avec une "grande précision" car dès qu'on fait 1 + 1, même avec 64 bits de précision, un float va faire une erreur de calcul. Avec un type Decimal, ce n'est pas le cas, à moins qu'on ne dépasse le nombre de chiffres pour la représentation. (gare au /3 par exemple)
Marsh Posté le 18-09-2006 à 15:56:21
Je reprécise : BigDecimal c'est bien mieux que des int parce que dans ta compta, tu as des multiplication avec des fractions, et le temps de l'opération, tu as besoin (obligatoire) de précision supplémentaire. C'est vraiment important quand le calcul doit être exact.
Surtout avec des float, quand tu mélanges des nombres d'ordre très différents, ça devient très tendu.
Marsh Posté le 18-09-2006 à 15:56:35
moustik510 a écrit : Ah ... ça me dis quelque chose ça. on a du me le dire pendant les cours... mais j'ai pas du le noter. Et je n'utilise pas Java professionnellement (dev SAP) donc j'évolue pas. |
quitte a mettre en place un systeme de ce type , garde toi un peu de marge : multiplie pas 1 000 ou 10 000
Marsh Posté le 18-09-2006 à 15:58:21
oui BigDecimal c'est comme celui du SQL.
je connais pas Decimal en java, juste java.math.BigDecimal.
Marsh Posté le 18-09-2006 à 16:00:05
A priori, y'a pas de raison pour que Decimal ne soit pas pareil que BigDecimal donc
Sauf que l'un doit avoir une précision de 40 chiffres alors que l'autre n'a une précision que de 20 chiffres mettons. (à compter que Decimal existe hein, moi je ne connais pas Javs )
Toujours est-t-il que dans ce cas, je suis d'accord avec ta solution. BigDecimal est dont parfaitement adapté, car garantie une précision fixe, ce qu'on utilise effectivement en compta.
Et oui, en compta :
10.00 / 3 * 3 = 9.99 et non 10 ! (d'où l'importance de travailler en précision fixe)
Marsh Posté le 18-09-2006 à 16:24:41
Ben forcément
C malin aussi d'appeler un champ Bigxxx alors que le type "de base" n'existe pas
Marsh Posté le 28-10-2006 à 18:12:55
Taz a écrit : l'imprécision se propage et grandit linéairement. |
Il n'y a pas de raison que l'évolution de l'imprécision soit linéaire
Sinon, juste pour compléter les réponses, un nombre à virgule flottante est composé de 3 "champs": un bit de signe, un exposant, une mantisse (S|E|M).
Pour un float, 32 bits, répartis comme ceci : (1|8|23).
Pour un double, 64 bits, répartis comme ceci : (1|11|52).
Le nombre de chiffres significatifs est donc de 23 en base 2 pour un float (donc 23 * ln2/ln10 ~= 7 en base 10) et de 52 en base 2 pour un double (donc 52 * ln2/ln10 ~= 16 en base 10).
Ca c'est pour le stockage "brut". Après, c'est les opérations qui font perdre de l'imprécision, notamment l'addition de 2 nombres d'ordre de grandeur très différents (absorption) ou la soustraction de 2 nombres très très proches (cancellation).
Les multiplications n'entraîne pas de perte d'information autre que celle du stockage du résultat.
Marsh Posté le 08-11-2006 à 09:54:39
rom1v a écrit : Il n'y a pas de raison que l'évolution de l'imprécision soit linéaire |
tu as raison, c'est toujours pire que linéaire.
Marsh Posté le 08-11-2006 à 10:08:27
Petite correction par rapport à ce que j'ai dit, le nombre de chiffres significatifs est la taille de la mantisse + 1 (car il y a un 1 implicite), et donc pour un float, 23 chiffres significatifs en base 2, et en double 53...
Marsh Posté le 18-09-2006 à 11:08:21
[résolu] : On va multiplier par 100, merci
Bonjour,
Petits soucis : je me relance dans la prog Java avec quelques oublis....
Je récupère dans un fichier un montant (float) et un nombre (int).
Je récupère donc cela dans des chaines de caractères que je convertis ensuite en float ou int.
float montant = Float.parseFloat("7.7" ); //Exemple d'une valeur que je peux avoir
int nombre = Integer.parseInt("3" );
// Ensuite produit classique
float total = montant * nombre.
[#f0000e]Et là, au lieu d'avoir 23.1, j'ai 23,09.............. (pleins de chiffre derrières)
??? Une idée ?
Merci d'avance.
Message édité par moustik510 le 18-09-2006 à 12:04:57