Java中BigDecimal精度和相等比較的坑
為什么要有BigDecimal ,他是干什么的
float和double類型的主要設(shè)計目標(biāo)是為了科學(xué)計算和工程計算。他們執(zhí)行二進(jìn)制浮點運算,這是為了在廣域數(shù)值范圍上提供較為精確的快速近似計算而精心設(shè)計的。然而,它們沒有提供完全精確的結(jié)果,所以不應(yīng)該被用于要求精確結(jié)果的場合。但是,商業(yè)計算往往要求結(jié)果精確,這時候就要使用BigDecimal啦。
什么是BigDecimal
BigDecimal 由任意精度的整數(shù)非標(biāo)度值 和32 位的整數(shù)標(biāo)度 (scale) 組成。如果為零或正數(shù),則標(biāo)度是小數(shù)點后的位數(shù)。如果為負(fù)數(shù),則將該數(shù)的非標(biāo)度值乘以 10 的負(fù)scale 次冪。因此,BigDecimal表示的數(shù)值是(unscaledValue × 10-scale)。
本文將給大家詳細(xì)介紹關(guān)于Java中BigDecimal精度和相等比較的坑,下面話不多說了,來一起看看詳細(xì)的介紹吧
先想一下,創(chuàng)建BigDecimal對象的時候一般是怎么創(chuàng)建的?
- new一個,傳進(jìn)去值
- BigDecimal.valueOf方法,傳進(jìn)去值
作為一個數(shù)字類型,經(jīng)常有的操作是比較大小,有一種情況是比較是否相等。用equal方法還是compareTo方法?這里就是一個大坑
//new 傳進(jìn)去一個double
BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.equals(newZero));
//new 傳進(jìn)去一個字符串
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.equals(stringNewZero));
//valueOf 傳進(jìn)去一個double
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.equals(noScaleZero));
//valueOf 傳進(jìn)去一個double,再手動設(shè)置精度為1
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.equals(scaleZero));
用于比較的值全都是0,猜一猜上面幾個equals方法返回的結(jié)果是什么?全都是true?no no no...
true
false
false
false
驚不驚喜,意不意外?原因是什么呢?看一下BigDecimal的equals方法的實現(xiàn):
public boolean equals(Object x) {
//類型不同,直接返回false
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
//同一個對象,直接返回true
if (x == this)
return true;
//精度不同,直接返回false??!
if (scale != xDec.scale)
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)
return xs == compactValFor(this.intVal);
return this.inflated().equals(xDec.inflated());
}
從前面三個簡單的判斷就可以看出來,debug跟一下就知道是上面equals方法有三個返回false,都是因為精度不同。那么BigDecimal.ZERO的精度是多少呢?看下源碼:
// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
new BigDecimal(BigInteger.ZERO, 0, 0, 1),
new BigDecimal(BigInteger.ONE, 1, 0, 1),
new BigDecimal(BigInteger.valueOf(2), 2, 0, 1),
new BigDecimal(BigInteger.valueOf(3), 3, 0, 1),
new BigDecimal(BigInteger.valueOf(4), 4, 0, 1),
new BigDecimal(BigInteger.valueOf(5), 5, 0, 1),
new BigDecimal(BigInteger.valueOf(6), 6, 0, 1),
new BigDecimal(BigInteger.valueOf(7), 7, 0, 1),
new BigDecimal(BigInteger.valueOf(8), 8, 0, 1),
new BigDecimal(BigInteger.valueOf(9), 9, 0, 1),
new BigDecimal(BigInteger.TEN, 10, 0, 2),
};
/**
* The value 0, with a scale of 0.
*
* @since 1.5
*/
public static final BigDecimal ZERO = zeroThroughTen[0];
BigDecimal.ZERO值為0,精度為0.
而上面幾種返回false的case,都是因為精度不同。精度不同的原因,則是BigDecimal對象初始化的方式不同,從源碼上看,前三種初始化的方式都不同。
所以說,BigDecimal比較大小,還是用compareTo方法比較靠譜,改為compareTo之后,上面四個case返回的結(jié)果都是相等:
BigDecimal newZero = new BigDecimal(0.0);
System.out.println(BigDecimal.ZERO.compareTo(newZero));
BigDecimal stringNewZero = new BigDecimal("0.0");
System.out.println(BigDecimal.ZERO.compareTo(stringNewZero));
BigDecimal noScaleZero = BigDecimal.valueOf(0.0);
System.out.println(BigDecimal.ZERO.compareTo(noScaleZero));
BigDecimal scaleZero = BigDecimal.valueOf(0.0).setScale(1);
System.out.println(BigDecimal.ZERO.compareTo(scaleZero));
輸出結(jié)果
0
0
0
0
由此聯(lián)想到的一個更大的坑是,如果將BigDecimal的值作為HashMap的key,因為精度的問題,相同的值就可能出現(xiàn)hashCode值不同并且equals方法返回false,導(dǎo)致put和get就很可能會出現(xiàn)相同的值但是存取了不同的value。
再想一想,小數(shù)類型在計算機(jī)中本來就不能精確存儲,再把其作為HashMap的key就相當(dāng)不靠譜了,以后還是少用。
另外需要注意的一點是,寫代碼調(diào)別人寫的方法時,最好是點進(jìn)去看一下實現(xiàn)。再小再常用的方法,都可能埋著大坑
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Java實現(xiàn)飛機(jī)大戰(zhàn)游戲?附完整源碼
這篇文章主要介紹了Java實現(xiàn)飛機(jī)大戰(zhàn)游戲,本文給大家分享完整源代碼和效果圖展示,對java飛機(jī)大戰(zhàn)游戲?qū)崿F(xiàn)代碼感興趣的朋友一起看看吧2022-05-05
JAVA錯誤:'無效目標(biāo)發(fā)行版?17'的解決方案
這篇文章主要給大家介紹了關(guān)于JAVA錯誤:'無效目標(biāo)發(fā)行版?17'的解決方案,文中通過圖文介紹的非常詳細(xì),對大家學(xué)習(xí)或使用java具有一的的參考學(xué)習(xí)價值,需要的朋友可以參考下2022-09-09
Hadoop運行時遇到j(luò)ava.io.FileNotFoundException錯誤的解決方法
今天給大家?guī)淼氖顷P(guān)于Java的相關(guān)知識,文章圍繞著Hadoop運行時遇到j(luò)ava.io.FileNotFoundException錯誤展開,文中有非常詳細(xì)的解決方法,需要的朋友可以參考下2021-06-06
Spring Cache和EhCache實現(xiàn)緩存管理方式
這篇文章主要介紹了Spring Cache和EhCache實現(xiàn)緩存管理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Spring中HandlerMethod類源碼詳細(xì)解析
這篇文章主要介紹了Spring中HandlerMethod類源碼詳細(xì)解析,HandlerMethod類用于封裝控制器方法信息,包含類信息、方法Method對象、參數(shù)、注解等信息,具體的接口請求是可以根據(jù)封裝的信息調(diào)用具體的方法來執(zhí)行業(yè)務(wù)邏輯,需要的朋友可以參考下2023-11-11
關(guān)于報錯IDEA Terminated with exit code
如果在IDEA構(gòu)建項目時遇到下面這樣的報錯IDEA Terminated with exit code 1,那必然是Maven的設(shè)置參數(shù)重置了,導(dǎo)致下載錯誤引起的,本文給大家分享兩種解決方法,需要的朋友可以參考下2022-08-08
Spring?Boot與Spring?MVC?Spring對比及核心概念
這篇文章主要為大家介紹了Spring?Boot與Spring?MVC?Spring的對比以及你需要了解的核心概念,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03

