„New BigDecimal (13.3D)“ führt zu ungenau „13,3000000000000007105 ..“?
-
19-08-2019 - |
Frage
Wie kommt es, dass die Java BigDecimal
kann dies schmerzhaft sein?
Double d = 13.3D;
BigDecimal bd1 = new BigDecimal(d);
BigDecimal bd2 = new BigDecimal(String.valueOf(d));
System.out.println("RESULT 1: "+bd1.toString());
System.out.println("RESULT 2: "+bd2.toString());
RESULT 1: 13.300000000000000710542735760100185871124267578125
RESULT 2: 13.3
Gibt es eine Situation, in der ein Ergebnis erwünscht wäre? Ich weiß, dass Java 1.5 die toString()
Methode geändert, aber das war die beabsichtigte Konsequenz?
Auch merke ich, dass BigDecimal
doubleValue()
etc, aber die Bibliothek, die ich mit helfend arbeite verwendet eine toString()
und das kann ich nicht ändern: - (
Prost.
Lösung
Nun, die API Genügt diese scheinbare Widersprüchlichkeit im Konstruktor BigDecimal(double val)
:
Die Ergebnisse dieses Konstruktor können etwas unberechenbar sein. Man könnte davon ausgehen, dass das Schreiben neuer BigDecimal (0.1) in Java erstellt ein BigDecimal, die genau gleich ist, 0,1 (ein nicht-skalierte Wert von 1, mit einer Skala von 1), aber es ist tatsächlich gleich zu 0,1000000000000000055511151231257827021181583404541015625. Dies liegt daran, dass 0,1 kann nicht sein, genau wie ein Doppel dargestellt (oder, was das betrifft, als ein binärer Bruch jeder endlicher Länge). Somit wird der Wert daß Weise wird die übergebenen Konstruktor ist nicht genau gleich 0,1, Erscheinungen ungeachtet.
Das String-Konstruktor, auf der anderen Seite, ist vollkommen vorhersehbar: Schreiben neue BigDecimal ( „0,1“) erstellt ein BigDecimal, die genau gleich ist 0,1, wie man erwarten würde. Daher ist es in der Regel, dass die empfohlene String-Konstruktor verwendet werden, in Vorzug dieser.
Wenn ein Doppel muss für eine BigDecimal , Anmerkung als Quelle verwendet werden, die Dieser Konstruktor stellt eine exakte Umwandlung; es gibt nicht die gleiche Ergebnis wie die doppelt auf eine Umwandlungs String mit der Double.toString (Doppel) Verfahren und dann unter Verwendung der BigDecimal (String) Konstrukteur. Um dieses Ergebnis zu erhalten, Verwendung die statische valueOf (Doppel) Methode .
Die Moral der Geschichte: Der Schmerz scheint selbstverschuldet, nur href verwenden <= "http://java.sun.com/javase/6/docs/api/java/math/BigDecimal.html#BigDecimal ( java.lang.String) "rel = "noreferrer"> new BigDecimal(String val)
oder BigDecimal.valueOf(double val)
statt =)
Andere Tipps
Ihr Problem hat nichts mit BigDecimal
zu tun, und alles, was mit Double
, die 13,3 nicht genau darstellen kann, da es Binärbrüche intern verwendet.
So Ihre Fehler werden in der ersten Zeile eingeführt. Die erste BigDecimal
einfach bewahrt, während String.valueOf()
hat einige fischig Runden, die die zweite führt den gewünschten Inhalt haben, ziemlich viel durch Glück.
Sie möchten sich darüber informieren, wie Fließkommawerte umgesetzt werden ( IEEE 754-1985 ). Und plötzlich wird alles werden kristallklar.
Dies ist nicht die Schuld der BigDecimal
- es ist die Schuld von double
ist. BigDecimal
genau, die den genau Wert von d
. String.valueOf
wird nur das Ergebnis auf wenige Dezimalstellen zeigt.
Die Fraktionen mit binären Zahlentypen dargestellt (d. double
, float
) nicht genau in diesen Typen gespeichert werden.
Double d = 13.3;
BigDecimal bdNotOk = new BigDecimal(d);
System.out.println("not ok: " + bdNotOk.toString());
BigDecimal bdNotOk2 = new BigDecimal(13.3);
System.out.println("not ok2: " + bdNotOk2.toString());
double x = 13.3;
BigDecimal ok = BigDecimal.valueOf(x);
System.out.println("ok: " + ok.toString());
double y = 13.3;
// pretty lame, constructor's behavior is different from valueOf static method
BigDecimal bdNotOk3 = new BigDecimal(y);
System.out.println("not ok3: " + bdNotOk3.toString());
BigDecimal ok2 = new BigDecimal("13.3");
System.out.println("ok2: " + ok2.toString());
Double e = 0.0;
for(int i = 0; i < 10; ++i) e = e + 0.1; // some fractions cannot be accurately represented with binary
System.out.println("not ok4: " + e.toString()); // should be 1
BigDecimal notOk5 = BigDecimal.valueOf(e);
System.out.println("not ok5: " + notOk5.toString()); // should be 1
/*
* here are some fractions that can be represented exactly in binary:
* 0.5 = 0.1 = 1 / 2
* 0.25 = 0.01 = 1 / 4
* 0.75 = 0.11 = 3 / 4
* 0.125 = 0.001 = 1 / 8
*/
Ausgabe:
not ok: 13.300000000000000710542735760100185871124267578125
not ok2: 13.300000000000000710542735760100185871124267578125
ok: 13.3
not ok3: 13.300000000000000710542735760100185871124267578125
ok2: 13.3
not ok4: 0.9999999999999999
not ok5: 0.9999999999999999
Verwenden Sie einfach BigDecimal.valueOf(d)
oder new BigDecimal(s)
.