在使用BigDecimal的除法時,遇到一個鬼畜的問題,本以為的精度計算,結果使用返回0,當然最終發現還是使用姿勢不對導致的,因此記錄一下,避免后面重蹈覆轍
I. 問題拋出
在使用BigDecimal做高精度的除法時,一不注意遇到了一個小問題,如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
@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不是高精度的計算么,講道理不應該不會出現這種整除的問題吧
我們知道在BigDecimal做觸發時,可以指定保留小數的參數,如果加上這個,是否會不一樣呢?
1
2
3
4
5
|
BigDecimal origin = new BigDecimal( 541253 ); BigDecimal now = new BigDecimal( 12389431 ); BigDecimal val = origin.divide(now, 5 , RoundingMode.HALF_UP); System.out.println(val); |
輸出結果為:
0.04369
所以說在指定了保留小數之后,則沒有問題,所以大膽的猜測一下,是不是上面的幾種case中,由于scale值沒有指定時,默認值不一樣,從而導致最終結果的精度不同呢?
簡單的深入源碼分析一下,執行的方式為 origin.divide(now, RoundingMode.HALF_UP);, 所以這個scale參數就瞄準origin對象,而這個對象,就只能去分析它的構造了,因為沒有其他的地方使用
II. 源碼定位
1. 整形傳參構造
分析下面這一行, 直接進入源碼
1
|
BigDecimal origin = new BigDecimal( 541253 ); |
很明顯的int傳參構造,進去簡單看一下
1
2
3
4
5
6
7
8
9
10
11
12
|
// 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為正數時,以它進行的除法,不現實指定scale參數時,最終返回的都是沒有小數的,同樣看一眼,還有long的傳參方式, BigInteger也一樣
2. 浮點傳參
接下來就是浮點的scale默認值確認了,這個構造相比前面的復雜一點,源碼就不貼了,太長,也看不太懂做了些啥,直接用猥瑣一點的方式,進入debug模式,單步執行
1
2
3
4
5
6
|
@Test public void testBigDecimal() { BigDecimal origin = new BigDecimal( 541253.0 ); BigDecimal now = new BigDecimal( 12389431.1 ); BigDecimal tmp = new BigDecimal( 0.0 ); } |
根據debug的結果,第一個,scale為0; 第二個scale為29, 第三個scale為0
3. String傳參
依然是一大串的邏輯,同樣采用單步debug的方式試下
1
2
3
4
5
6
|
@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. 小結
對于BigDecimal進行除法運算時,最好指定其scale參數,不然可能會有坑
對于BigDecimla的scale初始化的原理,有待深入看下BigDecimal是怎么實現的
最后貼一張乘法的圖作為收尾
到此這篇關于Java中BigDecimal除法使用不當導致精度問題的文章就介紹到這了,更多相關Java BigDecimal除法精度內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://juejin.cn/post/7025607161883394079