[Java] BigDecimal 精度計算

[Java] BigDecimal 精度計算

問題

實務上,我們在程式中處理與金錢相關的議題不會使用浮點數,因為那會造成 Truncate Error,算出來的金額會有浮點數誤差,通常我們會使用 BigDecimal 來計算,提高計算的精度。

而我曾經要計算某金額除以匯率,計算結果四捨五入小數點取到第二位,於是我就寫了以下範例,使用 BigDecimal 除法,分別填入除數與 MathContext , MathContext 的建構子的第一參數 setPrecision ,我以為設定的是小數點的精度,但事與願達,結果是2.4E2即 240 以科學記號表示。

1
2
3
4
BigDecimal dividend = new BigDecimal("999999.9999");
BigDecimal divisor = new BigDecimal("4096.00");
BigDecimal result = dividend.divide(divisor, new MathContext(2, RoundingMode.HALF_UP));
System.out.println(result);

Scale, Precision傻傻分不清楚

由於上述計算結果並不是我想要,我在網路上找到 BigDecimal 對 Scale , Precision 的介紹,我才赫然發現一開始我就搞錯用法了,原來 BigDecimal 也像 IEEE-754 採用類似的表示方式。

BigDecimal表示法

Java BigDecimal 是由兩個數值所構成的,分別是隨機的精度整數、跟 32 位元整數範圍,通常BigDecimal以表示,其中n為小數位數。

Precision

精度定義是未經 BigDecimal 正規表示的數值之位數,即該數值整數加上小數的數位,例如: 123.45 ,此精度會回傳 5 。

Scale

範圍定義是經 BigDecimal 正規表示後的小數位數,如果 Scale 是零、正整數,則會將該數的數個位數挪動到小數點符號的右邊,如若 Scale 是負整數,則該值會乘上 10 的幂次,例如: 12345 , scale = 2,則回傳 123.45 , 12345 , scale = -1 ,則回傳 123450 。

解決方法

承上述例子,因為精度是設定 2 ,舊版的寫法會造成四捨五入到十位數,十數位以下通通進位,並以科學記號表示。解決方法是改用 BigDecimal 另一個重載 divide 的方法,就可以在第二參數設定小數範圍為 2 ,最後結果為244.14

1
2
3
4
5
public class BigDecimal extends Number implements Comparable<BigDecimal> {
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) {
return divide(divisor, scale, roundingMode.oldMode);
}
}
1
2
3
4
BigDecimal dividend = new BigDecimal("999999.9999");
BigDecimal divisor = new BigDecimal("4096.00");
BigDecimal result = dividend.divide(divisor, 2, RoundingMode.HALF_UP);
System.out.println(result);