Kelas BigDecimal
memiliki beberapa metode yang berguna untuk menjamin konversi lossless:
byteValueExact()
shortValueExact()
intValueExact()
longValueExact()
Namun, metode floatValueExact()
dan doubleValueExact()
tidak ada.
Saya membaca kode sumber OpenJDK untuk metode floatValue()
dan doubleValue()
. Keduanya tampaknya mundur ke Float.parseFloat()
dan Double.parseDouble()
, masing-masing, yang dapat mengembalikan infinity positif atau negatif. Misalnya, parsing string 10.000 9s akan mengembalikan tak terhingga positif. Seperti yang saya pahami, BigDecimal
tidak memiliki konsep internal infinity. Selanjutnya, mengurai string 1009s sebagai double
memberi 1.0E100
, yang bukan tanpa batas, tetapi kehilangan presisi.
Apa itu implementasi yang masuk akal floatValueExact()
dan doubleValueExact()
?
Aku berpikir tentang double
solusi dengan menggabungkan BigDecimal.doubleValue()
, BigDecial.toString()
, Double.parseDouble(String)
dan Double.toString(double)
, tapi tampaknya berantakan. Saya ingin bertanya di sini karena mungkin ada (harus!) Solusi yang lebih sederhana.
Untuk lebih jelasnya, saya tidak perlu solusi kinerja tinggi.
sumber
Jawaban:
Dari membaca yang docs , semua hal ini dengan
numTypeValueExact
varian adalah untuk memeriksa keberadaan sebagian kecil atau jika nilai terlalu besar untuk jenis numerik dan melemparkan pengecualian.Adapun
floatValue()
dandoubleValue()
, pemeriksaan overflow serupa sedang dilakukan, tetapi alih-alih melempar pengecualian, malah mengembalikanDouble.POSITIVE_INFINITY
atauDouble.NEGATIVE_INFINITY
untuk ganda danFloat.POSITIVE_INFINITY
atauFloat.NEGATIVE_INFINITY
untuk mengapung.Karenanya implementasi
exact
metode yang paling masuk akal (dan paling sederhana) untuk float dan double, harus cukup memeriksa apakah konversi kembaliPOSITIVE_INFINITY
atauNEGATIVE_INFINITY
.Selain itu , ingatlah bahwa
BigDecimal
itu dirancang untuk menangani kurangnya presisi yang berasal dari menggunakanfloat
ataudouble
untuk irasional besar, oleh karena itu seperti @JB Nizet berkomentar , cek lain yang dapat Anda tambahkan di atas adalah untuk mengubahdouble
ataufloat
kembaliBigDecimal
untuk melihat apakah Anda masih mendapatkan nilai yang sama. Ini harus membuktikan konversi itu benar.Inilah yang akan terlihat seperti metode untuk
floatValueExact()
:Penggunaan
compareTo
bukannya diequals
atas disengaja agar tidak menjadi terlalu ketat dengan cek.equals
hanya akan mengevaluasi true ketika duaBigDecimal
objek memiliki nilai dan skala yang sama (ukuran bagian pecahan desimal), sedangkancompareTo
akan mengabaikan perbedaan ini ketika tidak penting. Misalnya2.0
vs2.00
.sumber
Float.isFinite()
atauFloat.isInfinite()
, tetapi ini opsional.:)
123.456f
. Saya kira ini disebabkan oleh perbedaan ukuran signifikansi (mantissa) antara float 32-bit dan 64-bit. Dalam kode di atas Anda, saya mendapatkan hasil yang lebih baik dengan:if (new BigDecimal(result, MathContext.DECIMAL32).equals(decimal)) {
. Apakah ini perubahan yang masuk akal ... atau apakah saya kehilangan case sudut lain dari nilai floating point?123.456f
secara konseptual sama dengan contoh 1.0E100 Anda. Itu mengejutkan saya bahwa jika Anda perlu memeriksa bahwa konversi dari BigDecimal -> floating point biner adalah tepat maka yang perlu adalah masalahnya; yaitu konversi tidak boleh direnungkan.