Perhatian: Ganda ke BigDecimal di Java

Kombinasi dari basis pengembang Java yang besar di seluruh dunia dan dokumentasi API online yang mudah diakses telah menghasilkan dokumentasi Java SE API yang umumnya menyeluruh dan akurat. Masih ada sudut yang mungkin tidak selengkap atau seakurat yang diinginkan, tetapi dokumentasi API umumnya cukup bagus baik dalam hal ketelitian dan keakuratan.

Meskipun dokumentasi API berbasis Javadoc telah menjadi sangat berguna, kami para pengembang sering kali terburu-buru dan sering merasa sangat percaya diri dengan kemampuan kami sendiri sehingga hampir tak terhindarkan bahwa terkadang kami akan terus mencoba melakukan berbagai hal tanpa membaca manual terlebih dahulu. Karena kecenderungan ini, terkadang kita bisa terbakar dengan menyalahgunakan API tertentu meskipun ada dokumentasi yang memperingatkan kita untuk tidak (salah) menggunakannya dengan cara itu. Saya membahas ini di posting blog saya di Boolean.getBoolean (String) dan menyoroti masalah serupa di posting ini terkait dengan penggunaan konstruktor BigDecimal yang menerima ganda.

Pada pandangan pertama, mungkin tampak bahwa konstruktor BigDecimal yang menerima ganda Java akan menahannya dengan presisi yang ditentukan semula dalam semua kasus. Namun, pesan Javadoc untuk konstruktor ini secara eksplisit memperingatkan, "Hasil dari konstruktor ini bisa jadi tidak dapat diprediksi." Ini selanjutnya menjelaskan mengapa (double tidak dapat menahan presisi yang tepat dan ini menjadi bukti ketika diteruskan ke konstruktor BigDecimal) dan menyarankan agar konstruktor alternatif yang menerima String sebagai parameter digunakan sebagai gantinya. Dokumentasi juga mengusulkan penggunaan BigDecimal.valueOf (double) sebagai cara yang lebih disukai untuk mengonversi double atau float ke BigDecimal.

Daftar kode berikut digunakan untuk mendemonstrasikan prinsip-prinsip ini dan beberapa gagasan terkait.

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); } } 

Output dari menjalankan kode di atas ditampilkan di snapshot layar berikutnya.

Seperti yang ditunjukkan oleh output di atas, masalah pengecoran float menjadi double mencegah seseorang mempertahankan presisi yang diinginkan saat melewatkan float langsung ke BigDecimal.valueOf(double)metode. Sebuah String dapat digunakan sebagai perantara untuk mencapai hal ini yang ditunjukkan dalam contoh dan seperti yang ditunjukkan dengan cara yang serupa di Mengonversi Float ke Double dengan Cara yang Tidak Begitu Umum.

Perhatikan bahwa penggunaan implisit berat Groovy atas BigDecimal mengubah permainan sedikit saat menggunakan pengetikan Groovy dan dinamis. Saya mungkin akan menyentuhnya di posting blog mendatang. Untuk detail lebih lanjut tentang masalah floating-point (dan saya menekankan "detail"), lihat Yang Harus Diketahui Setiap Ilmuwan Komputer Tentang Aritmatika Floating-Point.

Artikel ini, "Perhatian: Double to BigDecimal in Java", awalnya diterbitkan oleh JavaWorld.