Klasa BigDecimal
ma kilka użytecznych metod gwarantujących bezstratną konwersję:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
Jednak metody floatValueExact()
i doubleValueExact()
nie istnieją.
Czytam kod źródłowy OpenJDK dla metod floatValue()
i doubleValue()
. Oba wydają się wracać odpowiednio do Float.parseFloat()
i Double.parseDouble()
, co może zwrócić dodatnią lub ujemną nieskończoność. Na przykład parsowanie ciągu 10 000 cyfr 9 zwróci dodatnią nieskończoność. Jak rozumiem, BigDecimal
nie ma wewnętrznej koncepcji nieskończoności. Ponadto parsowanie ciągu 100 9s double
daje 1.0E100
, co nie jest nieskończonością, ale traci precyzję.
Co to jest rozsądne wdrożenie floatValueExact()
i doubleValueExact()
?
Myślałem o double
rozwiązanie łącząc BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
i Double.toString(double)
, ale wygląda niechlujnie. Chcę zapytać tutaj, ponieważ może (musi!) Być prostsze rozwiązanie.
Żeby było jasne, nie potrzebuję rozwiązania o wysokiej wydajności.
źródło
Odpowiedzi:
Od czytania na dokumenty , wszystko robi z
numTypeValueExact
wariantów jest do sprawdzenia istnienia części frakcji lub jeśli wartość jest zbyt duża dla typu liczbowego i rzucać wyjątki.Jeśli chodzi o
floatValue()
idoubleValue()
, przeprowadzana jest podobna kontrola przepełnienia, ale zamiast zgłosić wyjątek, zamiast tego zwracaDouble.POSITIVE_INFINITY
lubDouble.NEGATIVE_INFINITY
dla podwójnych i /Float.POSITIVE_INFINITY
lubFloat.NEGATIVE_INFINITY
zmiennoprzecinkowych.Dlatego najbardziej rozsądna (i najprostsza) implementacja
exact
metod dla liczb zmiennoprzecinkowych i podwójnych powinna po prostu sprawdzić, czy konwersja zwracaPOSITIVE_INFINITY
lubNEGATIVE_INFINITY
.Ponadto pamiętaj, że
BigDecimal
został zaprojektowany, aby poradzić sobie z brakiem precyzji wynikającym z używaniafloat
lubdouble
dla dużych irracjonalnych, dlatego też, jak skomentował @JB Nizet , kolejnym sprawdzeniem, które możesz dodać do powyższego, byłoby przekonwertowanie lub powrót, aby sprawdzić, czy nadal otrzymujesz ta sama wartość. To powinno udowodnić, że konwersja była prawidłowa.double
float
BigDecimal
Oto, jak wyglądałaby taka metoda
floatValueExact()
:Zastosowanie
compareTo
zamiastequals
powyższego jest celowe, aby nie stać się zbyt surowym przy kontroli.equals
oceni prawdę tylko wtedy, gdy dwaBigDecimal
obiekty mają tę samą wartość i skalę (rozmiar części ułamkowej części dziesiętnej), natomiastcompareTo
przeoczy tę różnicę, gdy nie ma to znaczenia. Na przykład2.0
vs2.00
.źródło
Float.isFinite()
lubFloat.isInfinite()
, ale jest to opcjonalne.:)
123.456f
. Wydaje mi się, że wynika to z różnej wielkości znaczenia (mantysy) pomiędzy 32-bitowym float i 64-bitowym double. W swojej powyższym kodem, mam lepsze wyniki z:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. Czy to rozsądna zmiana ... czy brakuje mi innego przypadku narożnego wartości zmiennoprzecinkowych?123.456f
jest koncepcyjnie taki sam jak twój przykład 1.0E100. Uderza mnie, że jeśli musisz sprawdzić, czy konwersja z BigDecimal -> binarny zmiennoprzecinkowy jest dokładna, to taka potrzeba jest problemem; tzn. konwersja nie powinna być rozważana.