天天看點

180706-BigDecimal除法的精度問題

BigDecimal除法的精度問題

在使用BigDecimal的除法時,遇到一個鬼畜的問題,本以為的精度計算,結果使用傳回0,當然最終發現還是自己的使用姿勢不對導緻的,是以記錄一下,避免後面重蹈覆轍

在使用BigDecimal做高精度的除法時,一不注意遇到了一個小問題,如下

​<code>​ ​</code>​

​<code>​}​</code>​

上面的輸出是什麼 ?

為什麼前面兩個會是0呢,如果直接是 ​<code>​541253 / 12389431​</code>​ = 0 倒是可以了解, 但是BigDecimal不是高精度的計算麼,講道理不應該不會出現這種整除的問題吧

我們知道在BigDecimal做觸發時,可以指定保留小數的參數,如果加上這個,是否會不一樣呢?

​<code>​BigDecimal val = origin.divide(now, 5, RoundingMode.HALF_UP);​</code>​​<code>​System.out.println(val);​</code>​

輸出結果為:

是以說在指定了保留小數之後,則沒有問題,是以大膽的猜測一下,是不是上面的幾種case中,由于scale值沒有指定時,預設值不一樣,進而導緻最終結果的精度不同呢?

簡單的深入源碼分析一下,執行的方式為 ​<code>​origin.divide(now, RoundingMode.HALF_UP);​</code>​, 是以這個scale參數就瞄準origin對象,而這個對象,就隻能去分析它的構造了,因為沒有其他的地方使用

分析下面這一行, 直接進入源碼

很明顯的int傳參構造,進去簡單看一下

​<code>​public BigDecimal(long val) {​</code>​​<code>​this.intCompact = val;​</code>​​<code>​this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;​</code>​​<code>​this.scale = 0;​</code>​​<code>​}​</code>​

so,很明确的知道預設的scale為0,也就是說當origin為正數時,以它進行的除法,不現實指定scale參數時,最終傳回的都是沒有小數的,同樣看一眼,還有long的傳參方式, BigInteger也一樣

接下來就是浮點的scale預設值确認了,這個構造相比前面的複雜一點,源碼就不貼了,太長,也看不太懂做了些啥,直接用猥瑣一點的方式,進入debug模式,單步執行

依然是一大串的邏輯,同樣采用單步debug的方式試下

上面三個的scale都是1

對于BigDecimal進行除法運算時,最好指定其scale參數,不然可能會有坑

對于BigDecimla的scale初始化的原理,有待深入看下BigDecimal是怎麼實作的

最後貼一張乘法的圖作為收尾