Saya harus menghitung beberapa variabel floating point dan kolega saya menyarankan saya untuk menggunakan BigDecimal
alih-alih double
karena akan lebih tepat. Tapi saya ingin tahu apa itu dan bagaimana memanfaatkannya BigDecimal
?
Saya harus menghitung beberapa variabel floating point dan kolega saya menyarankan saya untuk menggunakan BigDecimal
alih-alih double
karena akan lebih tepat. Tapi saya ingin tahu apa itu dan bagaimana memanfaatkannya BigDecimal
?
Jawaban:
A BigDecimal
adalah cara yang tepat untuk merepresentasikan angka. A Double
memiliki presisi tertentu. Bekerja dengan dobel dari berbagai besaran (katakan d1=1000.0
dan d2=0.001
) dapat mengakibatkan 0.001
dijatuhkan secara bersamaan ketika dijumlahkan karena perbedaan besarnya sangat besar. Dengan BigDecimal
ini tidak akan terjadi.
Kerugiannya BigDecimal
adalah bahwa itu lebih lambat, dan itu sedikit lebih sulit untuk memprogram algoritma seperti itu (karena +
-
*
dan /
tidak kelebihan beban).
Jika Anda berurusan dengan uang, atau ketepatan adalah suatu keharusan, gunakan BigDecimal
. Kalau tidak, Doubles
cenderung cukup baik.
Saya sarankan membaca javadoc dari BigDecimal
seperti yang mereka lakukan menjelaskan hal-hal yang lebih baik daripada yang saya lakukan di sini :)
if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
BigDecimal
", Double akan memiliki lebih banyak "presisi" (lebih banyak digit).
Bahasa Inggris saya tidak bagus jadi saya hanya akan menulis contoh sederhana di sini.
double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);
BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);
Output program:
0.009999999999999998
0.01
Adakah yang masih ingin menggunakan dobel? ;)
System.out.println(0.003f - 0.002f);
BigDecimal tepat:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Ada dua perbedaan utama dari ganda:
Alasan Anda harus menggunakan BigDecimal untuk perhitungan moneter bukan karena ia dapat mewakili angka apa pun, tetapi ia dapat mewakili semua angka yang dapat direpresentasikan dalam konsep desimal dan yang mencakup hampir semua angka dalam dunia moneter (Anda tidak pernah mentransfer 1/3 $ untuk seseorang).
Jika Anda menuliskan nilai pecahan seperti 1 / 7
nilai desimal yang Anda dapatkan
1/7 = 0.142857142857142857142857142857142857142857...
dengan urutan tak terbatas 142857
. Karena Anda hanya dapat menulis jumlah digit terbatas, Anda pasti akan membuat kesalahan pembulatan (atau pemotongan).
Angka seperti 1/10
atau 1/100
dinyatakan sebagai angka biner dengan bagian fraksional juga memiliki jumlah digit tak terbatas setelah titik desimal:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
menyimpan nilai-nilai sebagai biner dan karena itu dapat memperkenalkan kesalahan semata-mata dengan mengubah angka desimal menjadi angka biner, tanpa melakukan aritmatika apa pun.
Angka desimal (seperti BigDecimal
), di sisi lain, menyimpan setiap angka desimal apa adanya. Ini berarti bahwa jenis desimal tidak lebih tepat daripada titik mengambang biner atau tipe titik tetap dalam arti umum (yaitu tidak dapat menyimpan 1/7
tanpa kehilangan presisi), tetapi lebih akurat untuk angka yang memiliki jumlah terbatas angka desimal sebagai sering terjadi untuk perhitungan uang.
Jawa BigDecimal
memiliki keuntungan tambahan yang dapat memiliki sewenang-wenang (namun terbatas) jumlah digit di kedua sisi titik desimal, hanya dibatasi oleh memori yang tersedia.
BigDecimal adalah perpustakaan numerik presisi arbitrer Oracle. BigDecimal adalah bagian dari bahasa Jawa dan berguna untuk berbagai aplikasi mulai dari yang finansial hingga yang ilmiah (di situlah saya ada).
Tidak ada yang salah dengan menggunakan ganda untuk perhitungan tertentu. Misalkan, Anda ingin menghitung Math.Pi * Math.Pi / 6, yaitu, nilai Riemann Zeta Function untuk argumen nyata dua (proyek yang sedang saya kerjakan). Divisi floating-point memberi Anda masalah kesalahan pembulatan yang menyakitkan.
BigDecimal, di sisi lain, mencakup banyak opsi untuk menghitung ekspresi dengan ketepatan arbitrer. Metode tambah, gandakan, dan bagi seperti dijelaskan dalam dokumentasi Oracle di bawah ini "take the place" dari +, *, dan / di BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
Metode compareTo sangat berguna untuk while dan untuk loop.
Namun, berhati-hatilah dalam penggunaan konstruktor untuk BigDecimal. Konstruktor string sangat berguna dalam banyak kasus. Misalnya, kodenya
BigDecimal onethird = BigDecimal baru ("0.33333333333");
menggunakan representasi string 1/3 untuk mewakili nomor berulang berulang hingga tingkat akurasi yang ditentukan. Kesalahan pembulatan kemungkinan besar terjadi di suatu tempat yang jauh di dalam JVM sehingga kesalahan pembulatan tidak akan mengganggu sebagian besar perhitungan praktis Anda. Namun, dari pengalaman pribadi saya, saya melihat banyak hal. Metode setScale penting dalam hal ini, seperti yang dapat dilihat dari dokumentasi Oracle.
/* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
Jika Anda berurusan dengan perhitungan, ada undang-undang tentang bagaimana Anda harus menghitung dan presisi apa yang harus Anda gunakan. Jika Anda gagal Anda akan melakukan sesuatu yang ilegal. Satu-satunya alasan sebenarnya adalah bahwa representasi bit dari kasus desimal tidak tepat. Seperti yang dikatakan Basil, sebuah contoh adalah penjelasan terbaik. Hanya untuk melengkapi contohnya, inilah yang terjadi:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Keluaran:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Kami juga memiliki itu:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}
Memberi kami hasilnya:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Tapi:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Memiliki output:
BigDec: 10 / 3 = 3.3333