Bazı kayan nokta değişkenleri hesaplamak zorunda ve meslektaşım beni kullanmak önermek BigDecimal
yerine double
daha kesin olacağından. Ama bunun ne olduğunu ve bundan en iyi şekilde nasıl yararlanabileceğini bilmek istiyorum BigDecimal
?
Bazı kayan nokta değişkenleri hesaplamak zorunda ve meslektaşım beni kullanmak önermek BigDecimal
yerine double
daha kesin olacağından. Ama bunun ne olduğunu ve bundan en iyi şekilde nasıl yararlanabileceğini bilmek istiyorum BigDecimal
?
Yanıtlar:
A BigDecimal
, sayıları temsil etmenin kesin bir yoludur. A'nın Double
kesin bir hassasiyeti vardır. Çeşitli büyüklüklerde çiftler ile çalışmak (diyelim d1=1000.0
ve söyleyin d2=0.001
) 0.001
, büyüklükteki fark çok büyük olduğu için toplanırken tamamen düşmesine neden olabilir . Bununla BigDecimal
olmazdı.
Dezavantajı BigDecimal
daha yavaş olmasıdır, ve (nedeniyle program algoritmaları bu şekilde biraz daha zor +
-
*
ve /
olmayan aşırı edilmiştir).
Parayla uğraşıyorsanız veya hassasiyet şartsa, kullanın BigDecimal
. Aksi takdirde Doubles
yeterince iyi olma eğilimindedir.
Okumayı tavsiye edersiniz javadoc ait BigDecimal
onlar daha iyi ben burada yapmak daha şeyleri açıklamak gibi :)
if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
BigDecimal
", bir Double daha fazla "hassasiyet" (daha fazla basamak) olurdu demek istiyorsun .
İngilizcem iyi değil, bu yüzden burada basit bir örnek yazacağım.
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);
Program çıktısı:
0.009999999999999998
0.01
Birisi hala çift mi kullanmak istiyor? ;)
System.out.println(0.003f - 0.002f);
BigDecimal kesindir :System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Çifte iki temel fark vardır:
Parasal hesaplamalar için BigDecimal kullanmanızın nedeni, herhangi bir sayıyı temsil edebilmesi değil, ondalık kavramda temsil edilebilen ve parasal dünyadaki neredeyse tüm sayıları içeren tüm sayıları temsil edebilmesidir (asla 1/3 $ aktarmazsınız) birine).
1 / 7
Ondalık değer gibi kesirli bir değer yazarsanız,
1/7 = 0.142857142857142857142857142857142857142857...
sonsuz bir dizi ile 142857
. Yalnızca sınırlı sayıda basamak yazabildiğiniz için, kaçınılmaz olarak bir yuvarlama (veya kesme) hatası verirsiniz.
Kesirli kısmı olan ikili sayılar gibi 1/10
veya 1/100
sayılarla ifade edilen sayılar ayrıca ondalık noktadan sonra sonsuz sayıda basamağa sahiptir:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
değerleri ikili olarak saklar ve bu nedenle herhangi bir aritmetik bile yapmadan yalnızca ondalık bir sayıyı ikili sayıya dönüştürerek hata verebilir.
Ondalık sayılar (örneğin BigDecimal
), her ondalık basamağı olduğu gibi saklar. Bu, ondalık türün genel anlamda bir ikili kayan noktadan veya sabit nokta türünden daha kesin olmadığı anlamına gelir (yani saklayamaz1/7
, kesinlik kaybı olmadan ), ancak sonlu ondalık basamağa sahip sayılar için daha doğrudur. genellikle para hesaplamaları için geçerlidir.
Java'nın BigDecimal
ondalık noktanın her iki tarafında rasgele (ancak sonlu) sayıya sahip olması ve yalnızca kullanılabilir bellekle sınırlı olması ek bir avantaja sahiptir.
BigDecimal, Oracle'ın keyfi hassas sayısal kütüphanesidir. BigDecimal, Java dilinin bir parçasıdır ve finanstan bilime kadar çeşitli uygulamalar için yararlıdır (işte burası).
Belirli hesaplamalar için çiftler kullanmanın yanlış bir yanı yoktur. Bununla birlikte, Math.Pi * Math.Pi / 6'yı, yani gerçek bir iki argüman için Riemann Zeta İşlevinin değerini (şu anda üzerinde çalıştığım bir proje) hesaplamak istediğinizi varsayalım. Kayan nokta bölümü size acı veren bir yuvarlama hatası sorunu sunar.
Öte yandan BigDecimal, ifadeleri keyfi hassasiyete hesaplamak için birçok seçenek içerir. Aşağıdaki Oracle belgelerinde açıklandığı gibi ekleme, çarpma ve bölme yöntemleri, BigDecimal Java World uygulamasında +, * ve / öğelerinin "yerini alır":
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
CompareTo yöntemi özellikle döngülerde ve için kullanışlıdır.
Ancak, BigDecimal için yapıcıları kullanırken dikkatli olun. Dize yapıcı birçok durumda çok yararlıdır. Örneğin, kod
BigDecimal onethird = yeni BigDecimal ("0.33333333333");
1 / 3'lük bir dize temsilini kullanarak, sonsuz sayıda yinelenen sayıyı belirli bir doğruluk derecesine kadar temsil eder. Yuvarlama hatası büyük olasılıkla JVM'nin içinde o kadar derin bir yerde ki yuvarlama hataları pratik hesaplamalarınızın çoğunu rahatsız etmeyecektir. Bununla birlikte, kişisel deneyimlerimden ötürü, yuvarlanma sürünme gördüm. Oracle belgelerinde görülebileceği gibi, setScale yöntemi bu açıdan önemlidir.
/* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
Hesaplamayla uğraşıyorsanız, nasıl hesaplamanız ve hangi hassasiyeti kullanmanız gerektiğine dair yasalar vardır. Eğer başarısız olursanız yasadışı bir şey yapacaksınız. Tek gerçek neden, ondalık vakaların bit sunumunun kesin olmamasıdır. Basil'in basitçe ifade ettiği gibi, bir örnek en iyi açıklamadır. Sadece örneğini tamamlamak için şöyle olur:
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)));
}
Çıktı:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Ayrıca:
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)));
}
Bize çıktıyı verir:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Fakat:
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)));
}
Çıktıya sahiptir:
BigDec: 10 / 3 = 3.3333