Class BigDecimal
verfügt über einige nützliche Methoden, um eine verlustfreie Konvertierung zu gewährleisten:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
Methoden floatValueExact()
und doubleValueExact()
existieren jedoch nicht.
Ich habe den OpenJDK-Quellcode für Methoden floatValue()
und gelesen doubleValue()
. Beide scheinen zu Rückfall Float.parseFloat()
und Double.parseDouble()
verbunden, die positive oder negative Unendlichkeit kann zurückzukehren. Wenn Sie beispielsweise eine Zeichenfolge von 10.000 9s analysieren, wird eine positive Unendlichkeit zurückgegeben. Soweit ich BigDecimal
weiß , gibt es kein internes Konzept der Unendlichkeit. Das Parsen einer Zeichenfolge von 100 9s double
ergibt 1.0E100
, was nicht unendlich ist, sondern an Präzision verliert.
Was ist eine vernünftige Implementierung floatValueExact()
und doubleValueExact()
?
Ich dachte an einer double
Lösung , die durch die Kombination von BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
und Double.toString(double)
, aber es sieht chaotisch. Ich möchte hier fragen, weil es eine einfachere Lösung geben kann (muss!).
Um klar zu sein, brauche ich keine Hochleistungslösung.
quelle
Antworten:
Vom Lesen der Dokumente , die alle mit dem tun
numTypeValueExact
Varianten sind die Existenz eines Bruchteils zu prüfen , oder wenn der Wert zu groß für den numerischen Typen ist und Ausnahmen werfen.Für
floatValue()
unddoubleValue()
wird eine ähnliche Überlaufprüfung durchgeführt, aber anstatt eine Ausnahme auszulösen, wird stattdessenDouble.POSITIVE_INFINITY
oderDouble.NEGATIVE_INFINITY
für Double und /Float.POSITIVE_INFINITY
oderFloat.NEGATIVE_INFINITY
Floats zurückgegeben.Daher sollte die vernünftigste (und einfachste) Implementierung der
exact
Methoden für float und double einfach prüfen, ob die KonvertierungPOSITIVE_INFINITY
oder zurückgibtNEGATIVE_INFINITY
.Außerdem , denken Sie daran , dass
BigDecimal
der Mangel an Präzision zu handhaben war entworfen , die aus der Verwendung kommtfloat
oderdouble
für große irrationalen daher als @JB Nizet kommentiert , eine weitere Überprüfung Sie die hinzufügen können oben wäre das zu konvertierendouble
oderfloat
wieder zuBigDecimal
sehen , wenn Sie noch erhalten der gleiche Wert. Dies sollte beweisen, dass die Konvertierung korrekt war.So würde eine solche Methode aussehen
floatValueExact()
:Die Verwendung von
compareTo
anstelle vonequals
oben ist beabsichtigt, um bei den Kontrollen nicht zu streng zu werden.equals
wird nur dann als wahr ausgewertet, wenn die beidenBigDecimal
Objekte den gleichen Wert und die gleiche Skalierung haben (Größe des Bruchteils der Dezimalstelle), währendcompareTo
dieser Unterschied übersehen wird, wenn er keine Rolle spielt. Zum Beispiel2.0
vs2.00
.quelle
Float.isFinite()
oder verwendenFloat.isInfinite()
, dies ist jedoch optional.:)
123.456f
. Ich denke, dies liegt an der unterschiedlichen Größe des Signifikanten (Mantisse) zwischen 32-Bit-Float und 64-Bit-Double. In Ihrem obigen Code erhalte ich bessere Ergebnisse mit :if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. Ist dies eine vernünftige Änderung ... oder fehlt mir ein weiterer Eckfall von Gleitkommawerten?123.456f
konzeptionell Ihrem 1.0E100-Beispiel. Es fällt mir auf, dass, wenn Sie überprüfen müssen , ob die Konvertierung von BigDecimal -> binärem Gleitkomma genau ist , diese Notwendigkeit das Problem ist; dh die Umwandlung sollte nicht in Betracht gezogen werden.