1. 程式人生 > >BigDecimal類的的基本用法【附加:整除報錯的解決方案】

BigDecimal類的的基本用法【附加:整除報錯的解決方案】

一提到Java裡面的商業計算,我們都知道不能用float和double,因為他們無法進行精確計算。但是Java的設計者給程式設計人員提供了一個很有用的類BigDecimal,他可以完善float和double類無法進行精確計算的缺憾。BigDecimal類位於java.maths類包下。首先我們來看下如何構造一個BigDecimal物件。它的建構函式很多,我挑最常用的兩個來演示一下:一個就是BigDecimal(double val),另一個就是BigDecimal(String str)。這兩個看上去沒什麼太大區別,但是正像API描述中說的那樣:

/*The results of this constructor can be somewhat unpredictable. One might assume that 
new BigDecimal(.1) is exactly equal to .1, but it is actually equal 
to .1000000000000000055511151231257827021181583404541015625. This is so because .1 
cannot be represented exactly as a double (or, for that matter, as a binary fraction 
of any finite length). Thus, the long value that is being passed in to the constructor 
is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal
(".1") is exactly equal to .1, as one would expect. Therefore, it is generally 
recommended that the (String) constructor be used in preference to this one.*/


也就是說利用double作為引數的建構函式,無法精確構造一個BigDecimal物件,需要自己指定一個上下文的環境,也就是指定精確位。而利用String物件作為引數傳入的建構函式能精確的構造出一個BigDecimal物件。請看下面的程式碼:

import java.math.*;

public class TestBigDecimal {
    public static void main(String args[]){
        BigDecimal bd = new BigDecimal("10.123");
        BigDecimal bd1 = new BigDecimal(10.123);
        

        System.out.println(bd +"/n"+ bd1);

    }
}

輸出:

10.123
10.1229999999999993320898283855058252811431884765625

所以我們在選擇建構函式時,要看具體需求而定。

另外,很多人會問到怎麼將基本型別,如int,float,double,long,和BigDecimal物件相互轉換。

很簡單:

基本型別通過建構函式轉換成對應的BigDecimal物件,而BigDecimal類提供了諸如intValue(), floatValue(), doubleValue(), longValue()方法來將BigDecimal物件轉換成對應的值。

關於BigDecimal是如何計算的,我以論壇中一個人的提問帖子為例,來簡單的寫出BigDecimal的運算方法。

題目是:李白無事街上走,提壺去買酒。遇店加一倍,見花喝一斗,五遇花和店,喝光壺中酒,試問李白壺中原有多少斗酒?

這道題應該是從後往前推,並且要逆運算,最後得出原有酒的體積。

import java.math.*;

public class Libai {
    public static void main(String args[]){
        BigDecimal volumn = new BigDecimal("0");
        
        for (int i=0; i<5;  i++){           
            volumn = volumn.add(new BigDecimal("1"));
            volumn = volumn.divide(new BigDecimal("2"));
        }
        
        System.out.print(volumn);
    }
}

結果:0.96875

金額的資料型別是BigDecimal
通過BigDecimal的divide方法進行除法時當不整除,出現無限迴圈小數時,就會拋異常的。

異常如下:

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(Unknown Source) 


應用場景:一批中供客戶的單價是1000元/年,如果按月計算的話1000/12=83.3333333333....

解決之道:就是給divide設定精確的小數點divide(xxxxx,2, BigDecimal.ROUND_HALF_EVEN)

在 java中, 四捨五入通過 BigDecimal 來實現。一定要注意:BigDecimal is Immutable。也就是跟String一樣,對前一個的修改,比如setScale(), add()等都會返回一個新的BigDecimal.四捨五入舍入模式是 BigDecimal.ROUND_HALF_UP 

下面貼上一張圖:

我來解釋:

roundMode是指舍位時候的模式,傳引數的時候用BigDecimal.ROUND_XXXX_XXX

ROUND_CEILING:   舍位時往正無窮方向移動   1.1-> 2   1.5-> 2   1.8-> 2   -1.1-> -1   -1.5-> -1   -1.8-> -1 
ROUND_DOWN:向0的方向移動 1.1-> 1   1.5-> 1   1.8-> 1   -1.1-> -1   -1.5-> -1   -1.8> -1 
ROUND_FLOOR:與CEILING相反,往負無窮   1.1-> 1   1.5-> 1   1.8-> 1   -1.1-> -2   -1.5-> -2   -1.8-> -2 
ROUND_HALF_DOWN:以5為分界線,或曰五舍六入 1.5-> 1   1.6-> 1   -1.5-> -1   -1.6-> -2   
ROUND_HALF_EVEN:同樣以5為分界線,如果是5,則前一位變偶數1.15-> 1.2   1.16-> 1.2   1.25-> 1.2   1.26-> 1.3 
ROUND_HALF_UP:最常見的四捨五入 
ROUND_UNNECESSARY:無需舍位 
ROUND_UP:與ROUND_DOWN,遠離0的方向 1.1-> 2   1.5-> 2   1.8-> 2   -1.1-> -2   -1.5-> -2   -1.8-> -2 

具體精確到幾位因該採用 
商=被除數.devide(除數,保留小數位數,精確方法) 

這篇文章我親生體會的。這個物件相當的好用,大家想了解更加深入點的話可以去檢視API