Java中BigDecimal除法使用不當(dāng)導(dǎo)致精度問(wèn)題
在使用BigDecimal的除法時(shí),遇到一個(gè)鬼畜的問(wèn)題,本以為的精度計(jì)算,結(jié)果使用返回0,當(dāng)然最終發(fā)現(xiàn)還是使用姿勢(shì)不對(duì)導(dǎo)致的,因此記錄一下,避免后面重蹈覆轍
I. 問(wèn)題拋出
在使用BigDecimal做高精度的除法時(shí),一不注意遇到了一個(gè)小問(wèn)題,如下
@Test
public void testBigDecimal() {
BigDecimal origin = new BigDecimal(541253);
BigDecimal now = new BigDecimal(12389431);
BigDecimal val = origin.divide(now, RoundingMode.HALF_UP);
System.out.println(val);
origin = new BigDecimal(541253);
now = new BigDecimal(12389431.3);
val = origin.divide(now, RoundingMode.HALF_UP);
System.out.println(val);
origin = new BigDecimal(541253.4);
now = new BigDecimal(12389431);
val = origin.divide(now, RoundingMode.HALF_UP);
System.out.println(val);
}
上面的輸出是什么 ?
0
0
0.043686703610520937021487456961257
為什么前面兩個(gè)會(huì)是0呢,如果直接是 541253 / 12389431 = 0 倒是可以理解, 但是BigDecimal不是高精度的計(jì)算么,講道理不應(yīng)該不會(huì)出現(xiàn)這種整除的問(wèn)題吧
我們知道在BigDecimal做觸發(fā)時(shí),可以指定保留小數(shù)的參數(shù),如果加上這個(gè),是否會(huì)不一樣呢?
BigDecimal origin = new BigDecimal(541253); BigDecimal now = new BigDecimal(12389431); BigDecimal val = origin.divide(now, 5, RoundingMode.HALF_UP); System.out.println(val);
輸出結(jié)果為:
0.04369
所以說(shuō)在指定了保留小數(shù)之后,則沒(méi)有問(wèn)題,所以大膽的猜測(cè)一下,是不是上面的幾種case中,由于scale值沒(méi)有指定時(shí),默認(rèn)值不一樣,從而導(dǎo)致最終結(jié)果的精度不同呢?
簡(jiǎn)單的深入源碼分析一下,執(zhí)行的方式為 origin.divide(now, RoundingMode.HALF_UP);, 所以這個(gè)scale參數(shù)就瞄準(zhǔn)origin對(duì)象,而這個(gè)對(duì)象,就只能去分析它的構(gòu)造了,因?yàn)闆](méi)有其他的地方使用
II. 源碼定位
1. 整形傳參構(gòu)造
分析下面這一行, 直接進(jìn)入源碼
BigDecimal origin = new BigDecimal(541253);
很明顯的int傳參構(gòu)造,進(jìn)去簡(jiǎn)單看一下
// java.math.BigDecimal#BigDecimal(int)
public BigDecimal(int val) {
this.intCompact = val;
this.scale = 0;
this.intVal = null;
}
public BigDecimal(long val) {
this.intCompact = val;
this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
this.scale = 0;
}
so,很明確的知道默認(rèn)的scale為0,也就是說(shuō)當(dāng)origin為正數(shù)時(shí),以它進(jìn)行的除法,不現(xiàn)實(shí)指定scale參數(shù)時(shí),最終返回的都是沒(méi)有小數(shù)的,同樣看一眼,還有l(wèi)ong的傳參方式, BigInteger也一樣
2. 浮點(diǎn)傳參
接下來(lái)就是浮點(diǎn)的scale默認(rèn)值確認(rèn)了,這個(gè)構(gòu)造相比前面的復(fù)雜一點(diǎn),源碼就不貼了,太長(zhǎng),也看不太懂做了些啥,直接用猥瑣一點(diǎn)的方式,進(jìn)入debug模式,單步執(zhí)行
@Test
public void testBigDecimal() {
BigDecimal origin = new BigDecimal(541253.0);
BigDecimal now = new BigDecimal(12389431.1);
BigDecimal tmp = new BigDecimal(0.0);
}
根據(jù)debug的結(jié)果,第一個(gè),scale為0; 第二個(gè)scale為29, 第三個(gè)scale為0



3. String傳參
依然是一大串的邏輯,同樣采用單步debug的方式試下
@Test
public void testBigDecimal() {
BigDecimal origin = new BigDecimal("541253.0");
BigDecimal now = new BigDecimal("12389431.1");
BigDecimal t = new BigDecimal("0.0");
}
上面三個(gè)的scale都是1
4. 小結(jié)
對(duì)于BigDecimal進(jìn)行除法運(yùn)算時(shí),最好指定其scale參數(shù),不然可能會(huì)有坑
對(duì)于BigDecimla的scale初始化的原理,有待深入看下BigDecimal是怎么實(shí)現(xiàn)的
最后貼一張乘法的圖作為收尾

到此這篇關(guān)于Java中BigDecimal除法使用不當(dāng)導(dǎo)致精度問(wèn)題的文章就介紹到這了,更多相關(guān)Java BigDecimal除法精度內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入了解Java核心類(lèi)庫(kù)--Arrays類(lèi)
這篇文章主要為大家詳細(xì)介紹了java Arrays類(lèi)定義與使用的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能給你帶來(lái)幫助2021-07-07
Java實(shí)現(xiàn)企業(yè)發(fā)放的獎(jiǎng)金根據(jù)利潤(rùn)提成問(wèn)題
這篇文章主要介紹了請(qǐng)利用數(shù)軸來(lái)分界,定位。注意定義時(shí)需把獎(jiǎng)金定義成長(zhǎng)整型,需要的朋友可以參考下2017-02-02
SpringCloud對(duì)服務(wù)內(nèi)某個(gè)client進(jìn)行單獨(dú)配置的操作步驟
我們的微服務(wù)項(xiàng)目用的是springCloud,某個(gè)微服務(wù)接口因?yàn)閿?shù)據(jù)處理量大,出現(xiàn)了接口超時(shí)的情況,我們需要單獨(dú)修改這一個(gè)feignClient的超時(shí)時(shí)間,所以本文介紹了SpringCloud對(duì)服務(wù)內(nèi)某個(gè)client進(jìn)行單獨(dú)配置的操作步驟,需要的朋友可以參考下2023-10-10
使用Spring-Retry解決Spring Boot應(yīng)用程序中的重試問(wèn)題
重試的使用場(chǎng)景比較多,比如調(diào)用遠(yuǎn)程服務(wù)時(shí),由于網(wǎng)絡(luò)或者服務(wù)端響應(yīng)慢導(dǎo)致調(diào)用超時(shí),此時(shí)可以多重試幾次。用定時(shí)任務(wù)也可以實(shí)現(xiàn)重試的效果,但比較麻煩,用Spring Retry的話一個(gè)注解搞定所有,感興趣的可以了解一下2023-04-04
Elasticsearch查詢(xún)之Match Query示例詳解
這篇文章主要為大家介紹了Elasticsearch查詢(xún)之Match查詢(xún)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Java基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)編程實(shí)例詳解
本文主要給大家介紹的是Java基于Socket實(shí)現(xiàn)網(wǎng)絡(luò)編程的實(shí)例,并給大家介紹了TCP與UDP傳輸協(xié)議,有需要的小伙伴可以來(lái)參考下2016-07-07
Sleuth+logback 設(shè)置traceid 及自定義信息方式
這篇文章主要介紹了Sleuth+logback 設(shè)置traceid 及自定義信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
如何解決HttpServletRequest.getInputStream()多次讀取問(wèn)題
這篇文章主要介紹了如何解決HttpServletRequest.getInputStream()多次讀取問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

