32 votes

La multiplication de deux nombres à l'aide de BigDecimal renvoie une valeur erronée

Exécution du code suivant :

new BigDecimal(0.06 * 3).toString()

renvoie à 0.179999999999999993338661852249060757458209991455078125 au lieu de 0.18 .

Exécuter

new BigDecimal(0.06).multiply(new BigDecimal(3)).toString() 

renvoie le même résultat.

Comment cela est-il possible ?

50voto

Jon Skeet Points 692016

Tu ne multiplies pas deux nombres en utilisant BigDecimal . Vous les multipliez en utilisant double et en transmettant le résultat à la fonction BigDecimal constructeur.

Vous voulez :

new BigDecimal("0.06").multiply(new BigDecimal("3")).toString()

Notez que vous faire vous souhaitez que les valeurs soient exprimées sous forme de chaînes de caractères - sinon, vous utilisez la fonction double pour 0,06, qui n'est pas exactement 0,06... vous avez perdu des informations avant même de commencer. (Vous n'avez pas vraiment besoin de la forme chaîne de 3, mais je l'ai fait par souci de cohérence).

Par exemple :

System.out.println(new BigDecimal(0.06));

imprime

0.059999999999999997779553950749686919152736663818359375

2voto

Daniel Trebbien Points 18089

Comme Jon Skeet l'écrit ci-dessus, la raison pour laquelle vous obtenez 0.179999999999999993338661852249060757458209991455078125 au lieu de 0.18 est parce que 0.06 * 3 est calculé comme un IEEE 754 double et ensuite ceci double est convertie en une valeur BigDecimal .

Même si 0.06 semble assez simple dans le code source, le nombre 0,06 n'est pas exactement représentable comme un IEEE 754. double Et alors ? 0.06 représente en fait un approximation de 0,06 égal à 0,05999999999999977795539507496869191527363818359375. Le nombre 0,06 en notation décimale n'est pas exactement représentable car le nombre est égal à 0b0.0 00011110101110000101 en notation binaire (où en gras représente une séquence de chiffres répétitifs). L'ordinateur doit tronquer cette séquence infinie de chiffres binaires, ce qui conduit à l'approximation 0,0599999....

Comme je l'ai détaillé dans ma réponse à la question concernant IEEE 754, 64 bits double ? vous pouvez utiliser ARIBAS ' decode_float() pour déterminer la mantisse et l'exposant d'un nombre à virgule flottante :

\==> set\_floatprec(double\_float).
-: 64

==> set\_printbase(2).
-: 0y10

==> decode\_float(0.06).
-: (0y11110101\_11000010\_10001111\_01011100\_00101000\_11110101\_11000010\_10001111, 
-0y1000100)

==> set\_printbase(10).
-: 10

==> -0y1000100.
-: -68

==> set\_floatprec(128).
-: 128

==> 1/2\*\*4 + 1/2\*\*5 + 1/2\*\*6 + 1/2\*\*7 + 1/2\*\*9 + 1/2\*\*11 + 1/2\*\*12 + 1/2\*\*13 + 1/2\*\*18 + 1/2\*\*20.
-: 0.11999\_98855\_59082\_03125\_00000\_00000\_00000\_00

( ** est une exponentiation en ARIBAS).

Et nous avons 0,06 = Σ i = 0..∞ 0.11999988555908203125 / 2 <sup>1 + 20 × i</sup>

Vous pouvez évaluer cette série dans un système de calcul formel tel que Maxima :

(%i1) sum ( 0.11999988555908203125 / 2 ^ (1 + 20 \* i), i, 0, inf ), simpsum;
(%o1)                                0.06

http://maxima-online.org/?inc=r760264757

1voto

dotvav Points 332

Mieux travailler avec BigDecimal#valueOf :

BigDecimal.valueOf(0.06).multiply(BigDecimal.valueOf(3))

imprime le résultat correct 0.18

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X