Achtung: Double to BigDecimal in Java

Die Kombination aus der großen weltweiten Entwicklerbasis von Java und der leicht zugänglichen Online-API-Dokumentation hat zu einer allgemein gründlichen und genauen Dokumentation der Java SE-API geführt. Es gibt immer noch Ecken, die möglicherweise nicht so gründlich oder genau sind, wie man es gerne hätte, aber die API-Dokumentation ist im Allgemeinen sowohl hinsichtlich der Gründlichkeit als auch der Genauigkeit ziemlich gut.

Obwohl die Javadoc-basierte API-Dokumentation ziemlich nützlich geworden ist, haben wir Entwickler es oft so eilig und fühlen uns oft so sicher in unseren eigenen Fähigkeiten, dass es fast unvermeidlich ist, dass wir manchmal weiterhin versuchen, Dinge zu tun, ohne zuerst das Handbuch zu lesen. Aufgrund dieser Tendenz können wir uns gelegentlich durch Missbrauch einer bestimmten API verbrennen, obwohl die Dokumentation uns davor warnt, sie auf diese Weise (falsch) zu verwenden. Ich habe dies in meinem Blog-Beitrag auf Boolean.getBoolean (String) besprochen und ein ähnliches Problem in diesem Beitrag im Zusammenhang mit der Verwendung des BigDecimal-Konstruktors hervorgehoben, der ein Double akzeptiert.

Auf den ersten Blick scheint es, dass der BigDecimal-Konstruktor, der ein Java-Double akzeptiert, es in allen Fällen mit seiner ursprünglich angegebenen Genauigkeit hält. In der Javadoc-Nachricht für diesen Konstruktor wird jedoch ausdrücklich gewarnt: "Die Ergebnisse dieses Konstruktors können etwas unvorhersehbar sein." Anschließend wird erklärt, warum (das Double kann nicht die genaue Genauigkeit halten und dies wird deutlich, wenn es an den BigDecimal-Konstruktor übergeben wird) und vorgeschlagen, stattdessen den alternativen Konstruktor zu verwenden, der einen String als Parameter akzeptiert. In der Dokumentation wird auch vorgeschlagen, BigDecimal.valueOf (double) als bevorzugte Methode zum Konvertieren eines Double oder Float in ein BigDecimal zu verwenden.

Die folgende Codeliste wird verwendet, um diese Prinzipien und einige verwandte Ideen zu demonstrieren.

DoubleToBigDecimal.java

import java.math.BigDecimal; import static java.lang.System.out; /** * Simple example of problems associated with using BigDecimal constructor * accepting a double. * * //marxsoftware.blogspot.com/ */ public class DoubleToBigDecimal { private final static String NEW_LINE = System.getProperty("line.separator"); public static void main(final String[] arguments) { // // Demonstrate BigDecimal from double // final double primitiveDouble = 0.1; final BigDecimal bdPrimDoubleCtor = new BigDecimal(primitiveDouble); final BigDecimal bdPrimDoubleValOf = BigDecimal.valueOf(primitiveDouble); final Double referenceDouble = Double.valueOf(0.1); final BigDecimal bdRefDoubleCtor = new BigDecimal(referenceDouble); final BigDecimal bdRefDoubleValOf = BigDecimal.valueOf(referenceDouble); out.println("Primitive Double: " + primitiveDouble); out.println("Reference Double: " + referenceDouble); out.println("Primitive BigDecimal/Double via Double Ctor: " + bdPrimDoubleCtor); out.println("Reference BigDecimal/Double via Double Ctor: " + bdRefDoubleCtor); out.println("Primitive BigDecimal/Double via ValueOf: " + bdPrimDoubleValOf); out.println("Reference BigDecimal/Double via ValueOf: " + bdRefDoubleValOf); out.println(NEW_LINE); // // Demonstrate BigDecimal from float // final float primitiveFloat = 0.1f; final BigDecimal bdPrimFloatCtor = new BigDecimal(primitiveFloat); final BigDecimal bdPrimFloatValOf = BigDecimal.valueOf(primitiveFloat); final Float referenceFloat = Float.valueOf(0.1f); final BigDecimal bdRefFloatCtor = new BigDecimal(referenceFloat); final BigDecimal bdRefFloatValOf = BigDecimal.valueOf(referenceFloat); out.println("Primitive Float: " + primitiveFloat); out.println("Reference Float: " + referenceFloat); out.println("Primitive BigDecimal/Float via Double Ctor: " + bdPrimFloatCtor); out.println("Reference BigDecimal/Float via Double Ctor: " + bdRefFloatCtor); out.println("Primitive BigDecimal/Float via ValueOf: " + bdPrimFloatValOf); out.println("Reference BigDecimal/Float via ValueOf: " + bdRefFloatValOf); out.println(NEW_LINE); // // More evidence of issues casting from float to double. // final double primitiveDoubleFromFloat = 0.1f; final Double referenceDoubleFromFloat = new Double(0.1f); final double primitiveDoubleFromFloatDoubleValue = new Float(0.1f).doubleValue(); out.println("Primitive Double from Float: " + primitiveDoubleFromFloat); out.println("Reference Double from Float: " + referenceDoubleFromFloat); out.println("Primitive Double from FloatDoubleValue: " + primitiveDoubleFromFloatDoubleValue); // // Using String to maintain precision from float to BigDecimal // final String floatString = String.valueOf(new Float(0.1f)); final BigDecimal bdFromFloatViaString = new BigDecimal(floatString); out.println("BigDecimal from Float via String.valueOf(): " + bdFromFloatViaString); } } 

Die Ausgabe der Ausführung des obigen Codes wird im nächsten Screenshot angezeigt.

Wie die obige Ausgabe zeigt, verhindert das Problem des Gießens von Float auf Double, dass die gewünschte Präzision beibehalten wird, wenn ein Float direkt an das BigDecimal.valueOf(double)Verfahren übergeben wird. Ein String kann als Vermittler verwendet werden, um dies zu erreichen. Dies wird im Beispiel gezeigt und auf ähnliche Weise beim Konvertieren von Float in Double auf nicht so übliche Weise demonstriert.

Beachten Sie, dass Groovys starke implizite Verwendung von BigDecimal das Spiel ein wenig verändert, wenn Sie Groovy und dynamisches Tippen verwenden. Ich werde das vielleicht in einem zukünftigen Blog-Beitrag ansprechen. Weitere Informationen zu Gleitkomma-Problemen (und ich betone "Details") finden Sie unter Was jeder Informatiker über Gleitkomma-Arithmetik wissen sollte.

Diese Geschichte "Achtung: Double to BigDecimal in Java" wurde ursprünglich von JavaWorld veröffentlicht.