Java中BigDecimal除法使用不當導致精度問題
在使用BigDecimal的除法時,遇到一個鬼畜的問題,本以為的精度計算,結(jié)果使用返回0,當然最終發(fā)現(xiàn)還是使用姿勢不對導致的,因此記錄一下,避免后面重蹈覆轍
I. 問題拋出
在使用BigDecimal做高精度的除法時,一不注意遇到了一個小問題,如下
@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
為什么前面兩個會是0呢,如果直接是 541253 / 12389431 = 0 倒是可以理解, 但是BigDecimal不是高精度的計算么,講道理不應該不會出現(xiàn)這種整除的問題吧
我們知道在BigDecimal做觸發(fā)時,可以指定保留小數(shù)的參數(shù),如果加上這個,是否會不一樣呢?
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
所以說在指定了保留小數(shù)之后,則沒有問題,所以大膽的猜測一下,是不是上面的幾種case中,由于scale值沒有指定時,默認值不一樣,從而導致最終結(jié)果的精度不同呢?
簡單的深入源碼分析一下,執(zhí)行的方式為 origin.divide(now, RoundingMode.HALF_UP);, 所以這個scale參數(shù)就瞄準origin對象,而這個對象,就只能去分析它的構(gòu)造了,因為沒有其他的地方使用
II. 源碼定位
1. 整形傳參構(gòu)造
分析下面這一行, 直接進入源碼
BigDecimal origin = new BigDecimal(541253);
很明顯的int傳參構(gòu)造,進去簡單看一下
// 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,很明確的知道默認的scale為0,也就是說當origin為正數(shù)時,以它進行的除法,不現(xiàn)實指定scale參數(shù)時,最終返回的都是沒有小數(shù)的,同樣看一眼,還有l(wèi)ong的傳參方式, BigInteger也一樣
2. 浮點傳參
接下來就是浮點的scale默認值確認了,這個構(gòu)造相比前面的復雜一點,源碼就不貼了,太長,也看不太懂做了些啥,直接用猥瑣一點的方式,進入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é)果,第一個,scale為0; 第二個scale為29, 第三個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"); }
上面三個的scale都是1
4. 小結(jié)
對于BigDecimal進行除法運算時,最好指定其scale參數(shù),不然可能會有坑
對于BigDecimla的scale初始化的原理,有待深入看下BigDecimal是怎么實現(xiàn)的
最后貼一張乘法的圖作為收尾
到此這篇關于Java中BigDecimal除法使用不當導致精度問題的文章就介紹到這了,更多相關Java BigDecimal除法精度內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)企業(yè)發(fā)放的獎金根據(jù)利潤提成問題
這篇文章主要介紹了請利用數(shù)軸來分界,定位。注意定義時需把獎金定義成長整型,需要的朋友可以參考下2017-02-02SpringCloud對服務內(nèi)某個client進行單獨配置的操作步驟
我們的微服務項目用的是springCloud,某個微服務接口因為數(shù)據(jù)處理量大,出現(xiàn)了接口超時的情況,我們需要單獨修改這一個feignClient的超時時間,所以本文介紹了SpringCloud對服務內(nèi)某個client進行單獨配置的操作步驟,需要的朋友可以參考下2023-10-10使用Spring-Retry解決Spring Boot應用程序中的重試問題
重試的使用場景比較多,比如調(diào)用遠程服務時,由于網(wǎng)絡或者服務端響應慢導致調(diào)用超時,此時可以多重試幾次。用定時任務也可以實現(xiàn)重試的效果,但比較麻煩,用Spring Retry的話一個注解搞定所有,感興趣的可以了解一下2023-04-04Elasticsearch查詢之Match Query示例詳解
這篇文章主要為大家介紹了Elasticsearch查詢之Match查詢示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04Java基于Socket實現(xiàn)網(wǎng)絡編程實例詳解
本文主要給大家介紹的是Java基于Socket實現(xiàn)網(wǎng)絡編程的實例,并給大家介紹了TCP與UDP傳輸協(xié)議,有需要的小伙伴可以來參考下2016-07-07Sleuth+logback 設置traceid 及自定義信息方式
這篇文章主要介紹了Sleuth+logback 設置traceid 及自定義信息方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07如何解決HttpServletRequest.getInputStream()多次讀取問題
這篇文章主要介紹了如何解決HttpServletRequest.getInputStream()多次讀取問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07