1. 程式人生 > >【java】使用BigDecimal計算時候注意事項

【java】使用BigDecimal計算時候注意事項

        一般資料在用BigDecimal自帶的運算方式時候是不會出現問題的,但是碰到了無限小數的時候,這個類直接運算方式就會出現一些取不到位或者溢位的錯誤。

錯誤1:Non-terminating decimal expansion; no exact representable decimal result

        這個錯誤翻譯過來大概是:十進位制小數點膨脹,沒有確切的表示的十進位制結果。其隱含的意義就是:無法結束的除法表示式;沒有精確的除結果。這樣的結果多出現在相除的數字最後結果是無限小數。例如:

BigDecimal num1 = new BigDecimal("10");
BigDecimal num2 = new BigDecimal("3");
BigDecimal num3 = num1.divide(num2);

        進行這類運算的時候,尤其我們在業務需求中計算金額的時候會碰到許多,那麼我們需要怎麼處理呢?其實通過上一篇部落格的分析,我們可以看出來,BigDecimal在處理數學運算的時候引入了scale這個量,就是小數度量的意思,然後我們在處理的時候可以選擇保留的位數,然後設定規範,從而讓程式自動捨去不要的數字。我們來看除法運算的一個過載:

BigDecimal.divide(BigDecimal divisor, int scale, RoundingMode roundingMode) ;

其中:scale為小數位數,roundingMode為小數模式。

所以我們只要指定了小數位數,然後再指定小數模式,就可以得到想要的結果了

保留小數的模式:

ROUND_CEILING
如果 BigDecimal 是正的,則做 ROUND_UP 操作;如果為負,則做 ROUND_DOWN 操作。
ROUND_DOWN
從不在捨棄(即截斷)的小數之前增加數字。
ROUND_FLOOR
如果 BigDecimal 為正,則作 ROUND_UP ;如果為負,則作 ROUND_DOWN 。
ROUND_HALF_DOWN
若捨棄部分> .5,則作 ROUND_UP;否則,作 ROUND_DOWN 。
ROUND_HALF_EVEN
如果捨棄部分左邊的數字為奇數,則作 ROUND_HALF_UP ;如果它為偶數,則作 ROUND_HALF_DOWN 。
ROUND_HALF_UP
若捨棄部分>=.5,則作 ROUND_UP ;否則,作 ROUND_DOWN 。
ROUND_UNNECESSARY
該“偽舍入模式”實際是指明所要求的操作必須是精確的,,因此不需要舍入操作。
ROUND_UP
總是在非 0 捨棄小數(即截斷)之前增加數字。

我們程式碼寫成這樣就不會有問題了,但是未知的位數被捨棄了:

BigDecimal num3 = num1.divide(num2,10,ROUND_HALF_DOWN);

其實這個保留小數位數還有另外一種寫法:

BigDecimal num3 = num1.divide(num2,10).setScale(2);

這樣寫也是可以保留兩位小數的,但是在沒有指定策略的情況下會碰到另外一個錯誤:

錯誤2: java.lang.ArithmeticException: Rounding necessary

這種就是沒有指定捨棄規則的錯誤了,總之加上就好了。

BigDecimal num3 = num1.divide(num2,10).setScale(2,BigDecimal.ROUND_HALF_UP);

或者

BigDecimal num3 = num1.divide(num2,10).setScale(2,RoundingMode.HALF_UP);

誤差1:業務上舍棄導致的在保留精度上少1的錯誤

        我們都知道,在進行捨棄或者進位的時候,我們其實是放棄了屬於數字的一部分精度上的數字,很顯然,我們如果要是做按照比例分錢或者是平賬一類的系統的時候會出現一些短賬的錯誤。

比如,三個人要均分一筆錢,假設是100元,那麼他們每個人所分得的錢都是100的三分之一。我們在計算的時候都取用滿5進一,不滿就捨去的保留演算法,假設結果就是兩位小數,那麼通過上述計算,每個人得到的錢都是33.33元,但是33.33*3=99.99,離100還差0.01,這就是在精度的位上差1的結果。

       但是現在還沒有很好的處理結果,一般這種業務上,兩方的除法操作,需要不同的舍取策略才能達到最後的平衡,比如一個用up一個用down,即一個任何情況都進位,一個任何情況都捨棄。但是這樣也會產生不公平的因素,但是金額的總數可以達到一個平衡。