J’ai le code suivant:
Double i=17.31; long j=(long) (i*100); System.out.println(j);
O / P: 1730 //Expected:1731
Double i=17.33; long j=(long) (i*100); System.out.println(j);
O / P: 1732 //Expected:1733
Double i=17.32; long j=(long) (i*100); System.out.println(j);
O / P: 1732 //Expected:1732{As expected}
Double i=15.33; long j=(long) (i*100); System.out.println(j);
O / P: 1533 //Expected:1533{as Expected}
J’ai essayé de Google mais incapable de trouver la raison. Je suis désolé si la question est sortingviale.
Aucune des réponses ne semble expliquer pourquoi 17.32
agi différemment.
La différence de comportement entre 17.32
et 17.33 & 17.31
est due aux règles d’ arrondi IEEE-754.
Règle d’arrondi appliquée: à partir de, la machine virtuelle Java ™ spécification §2.8.1
Les opérations d’arrondi de la machine virtuelle Java utilisent toujours le mode IEEE 754 rond vers le mode le plus proche. Les résultats inexacts sont arrondis à la valeur représentable la plus proche, les liens allant à la valeur avec un bit le moins significatif. C’est le mode par défaut IEEE 754. La machine virtuelle Java ne donne aucun moyen de modifier le mode d’arrondi en virgule flottante
Double est : (1 bit de signe + 11 bits d’exposant + 52 bits de fraction = 64 bits). Représentation interne après arrondi ci-dessous:
1 [63] 11 [62-52] 52 [51-00] Sign Exponent Fraction 17.31 --> 0 (+) 10000000011 (+4) 1.0001010011110101110000101000111101011100001010001111 17.32 --> 0 (+) 10000000011 (+4) 1.0001010100011110101110000101000111101011100001010010 //rounded up 17.33 --> 0 (+) 10000000011 (+4) 1.0001010101000111101011100001010001111010111000010100
17.31: (comparaison Mantissa)
Actual: 1.00010100111101011100001010001111010111000010100011110... Internal: 1.0001010011110101110000101000111101011100001010001111
17.32: (comparaison Mantissa)
Actual: 1.00010101000111101011100001010001111010111000010100011... Internal: 1.0001010100011110101110000101000111101011100001010010 //round-up!
17.33: (comparaison Mantissa)
Actual: 1.00010101010001111010111000010100011110101110000101000... Internal: 1.0001010101000111101011100001010001111010111000010100
17.31 -> 17.309999999999998721023075631819665431976318359375... 17.32 -> 17.32000000000000028421709430404007434844970703125... //(was rounded up) 17.33 -> 17.3299999999999982946974341757595539093017578125...
( Outil d’parsing IEEE-754 )
EDIT: Il y a un facteur plus en jeu lors de votre étape de multiplication, comme l’a dit @Jeppe Stig Nielsen. Le résultat de l’étape de multiplication FP ( référence ) fait son propre arrondi vers le plus proche. Cela change les résultats escomptés et ceux qui ne le sont pas, mais la raison est toujours la même que celle indiquée ci-dessus.
Enfin, en raison de la dissortingbution (long)
, une troncature se produit et vous laisse les résultats que vous voyez. (1730, 1732, 1732)
Conversion primitive rétrécissante: Spécification du langage Java ™ §5.1.3
Si le nombre à virgule flottante n’est pas un infini, la valeur à virgule flottante est arrondie à une valeur entière V, en arrondissant vers zéro en utilisant le mode de retour à zéro IEEE 754
La valeur double
est représentée non pas comme 17.31, mais comme 17.309999999999999. C’est pourquoi lorsque vous le multipliez par 100, vous obtenez 1730.99999999999999999. Après la conversion en Long
votre valeur double
est tronquée vers zéro. Donc, vous avez 1730.
Comme cela a été expliqué, cela est dû à une très petite précision en virgule flottante.
Cela peut être résolu en utilisant une commande Math.round (), comme suit:
long j=Math.round(i*100);
Cela permettra au programme de compenser les très petites erreurs qui sont héritées en utilisant des calculs en virgule flottante, en n’utilisant pas une opération de plancher, comme le fait la valeur par défaut (long).
Cela a à voir avec la représentation interne. Si vous regardez i * 100 dans le premier cas, vous verrez que c’est 1730.9999999999998. La dissortingbution ne supprimera que la partie après le point (tronqué).
Les réponses de Cthulhu et de svz sont correctes. Si vous souhaitez multiplier les doubles par 100 et éviter les erreurs d’arrondi en virgule flottante, vous pouvez utiliser Math.round()
pour arrondir le résultat le plus long
après chaque multiplication:
Double i=17.31; long j=Math.round(i*100); System.out.println(j);
Cela aura toujours une erreur en virgule flottante lors du traitement de doubles extrêmement grands (ou négatifs). Plus la valeur absolue d’un double est grande, plus la différence entre le double et le prochain double que Java peut représenter. Au bout d’un certain temps, les doubles consécutifs sont séparés par un nombre entier, et l’arrondi conventionnel ne pourra pas atténuer la différence. Pour les exemples que vous avez publiés, cela devrait fonctionner, cependant.
Lorsque vous faites ce genre de conversion longue, c’est le sol. Votre 17.31 pourrait effectivement être 17.30999999999 et c’est pourquoi il en est résulté 1730 au lieu de 1731.
utilisez i = i * 100, alors i.longValue () résoudra le problème.