180706-BigDecimal除法的精度問題

logo

BigDecimal除法的精度問題

在使用BigDecimal的除法時,遇到一個鬼畜的問題,本覺得的精度計算,結果使用返回0,固然最終發現仍是本身的使用姿式不對致使的,所以記錄一下,避免後面重蹈覆轍java

<!-- more -->git

I. 問題拋出

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

@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);
}

上面的輸出是什麼 ?ide

0
0
0.043686703610520937021487456961257

爲何前面兩個會是0呢,若是直接是 541253 / 12389431 = 0 卻是能夠理解, 可是BigDecimal不是高精度的計算麼,講道理不該該不會出現這種整除的問題吧源碼分析

咱們知道在BigDecimal作觸發時,能夠指定保留小數的參數,若是加上這個,是否會不同呢?學習

BigDecimal origin = new BigDecimal(541253);
BigDecimal now = new BigDecimal(12389431);

BigDecimal val = origin.divide(now, 5, RoundingMode.HALF_UP);
System.out.println(val);

輸出結果爲:this

0.04369

因此說在指定了保留小數以後,則沒有問題,因此大膽的猜想一下,是否是上面的幾種case中,因爲scale值沒有指定時,默認值不同,從而致使最終結果的精度不一樣呢?debug

簡單的深刻源碼分析一下,執行的方式爲 origin.divide(now, RoundingMode.HALF_UP);, 因此這個scale參數就瞄準origin對象,而這個對象,就只能去分析它的構造了,由於沒有其餘的地方使用code

II. 源碼定位

1. 整形傳參構造

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

BigDecimal origin = new BigDecimal(541253);

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

// 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模式,單步執行

@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

origin

now

tmp

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

smaple

4. 小結

  • 對於BigDecimal進行除法運算時,最好指定其scale參數,否則可能會有坑
  • 對於BigDecimla的scale初始化的原理,有待深刻看下BigDecimal是怎麼實現的

最後貼一張乘法的圖做爲收尾

mul

II. 其餘

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛

2. 聲明

盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

3. 掃描關注

QrCode

相關文章
相關標籤/搜索