[Java] BigDecimal 精度計算
問題
實務上,我們在程式中處理與金錢相關的議題不會使用浮點數,因為那會造成 Truncate Error,算出來的金額會有浮點數誤差,通常我們會使用 BigDecimal 來計算,提高計算的精度。
而我曾經要計算某金額除以匯率,計算結果四捨五入小數點取到第二位,於是我就寫了以下範例,使用 BigDecimal 除法,分別填入除數與 MathContext , MathContext 的建構子的第一參數 setPrecision ,我以為設定的是小數點的精度,但事與願達,結果是2.4E2
即 240 以科學記號表示。
1 | BigDecimal dividend = new BigDecimal("999999.9999"); |
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 | public class BigDecimal extends Number implements Comparable<BigDecimal> { |
1 | BigDecimal dividend = new BigDecimal("999999.9999"); |