一、BigDecimal簡介
float和double類型的主要設計目的是為了科學計算和工程計算。它們執行二進制浮點運算,這是為了在廣域數值范圍上提供較為精確的快速近似計算而精心設計的。然而,它們沒有提供完全精確的結果,所以不應該被用于要求精確結果的場合,而BigDecimal則適用于商業高精度計算 。
使用 float或者double精確地表示0.1(或者10的任何負數次方值)是不可能的,因為在二進制中是無法精確表示這些數字,就像十進制中無法精確表示1/3一樣。
System.out.println(1.03 – .42);
System.out.println(1.00 – 9 * .10);
//輸出結果:
0.6100000000000001
0.09999999999999998
二、BigDecimal用法
四個構造方法
- BigDecimal(int) 創建一個具有參數所指定整數值的對象。
- BigDecimal(double) 創建一個具有參數所指定雙精度值的對象。
- BigDecimal(long) 創建一個具有參數所指定長整數值的對象。
- BigDecimal(String) 創建一個具有參數所指定以字符串表示的數值的對象。
其中推薦使用的就是String參數的構造方法!!!!
- 參數類型為double的構造方法的結果有一定的不可預知性。有人可能認為在Java中寫入newBigDecimal(0.1)所創建的BigDecimal正好等于 0.1(非標度值 1,其標度為 1),但是它實際上等于0.1000000000000000055511151231257827021181583404541015625。這是因為0.1無法準確地表示為 double(或者說對于該情況,不能表示為任何有限長度的二進制小數)。這樣,傳入到構造方法的值不會正好等于 0.1(雖然表面上等于該值)。
- 另一方面,String 構造方法是完全可預知的:寫入 newBigDecimal(“0.1”) 將創建一個 BigDecimal,它正好等于預期的 0.1。因此,比較而言, 通常建議優先使用String構造方法。
- 當double必須用作BigDecimal的源時,請注意,此構造方法提供了一個準確轉換;它不提供與以下操作相同的結果:先使用Double.toString(double)方法,然后使用BigDecimal(String)構造方法,將double轉換為String。要獲取該結果,請使用static valueOf(double)方法。
常用的方法
- add(BigDecimal) BigDecimal對象中的值相加,然后返回這個對象。
- subtract(BigDecimal) BigDecimal對象中的值相減,然后返回這個對象。
- multiply(BigDecimal) BigDecimal對象中的值相乘,然后返回這個對象。
- divide(BigDecimal) BigDecimal對象中的值相除,然后返回這個對象。
- toString() 將BigDecimal對象的數值轉換成字符串。
- doubleValue() 將BigDecimal對象中的值以雙精度數返回。
- floatValue() 將BigDecimal對象中的值以單精度數返回。
- longValue() 將BigDecimal對象中的值以長整數返回。
- intValue() 將BigDecimal對象中的值以整數返回。
相除時的舍入方法
BigDecimal舍入模式
盡管數據庫存儲的是一個高精度的浮點數,但是通常在應用中展示的時候往往需要限制一下小數點的位數,比如兩到三位小數即可,這時就需要使用到setScale(int newScale, int roundingMode)函數,作為BigDecimal的公有靜態變量,舍入模式(Rounding Mode)的運算規則比較多,公有八種,這里作個說明,官方文檔也有介紹。
- ROUND_UP
向遠離零的方向舍入。舍棄非零部分,并將非零舍棄部分相鄰的一位數字加一。 - ROUND_DOWN
向接近零的方向舍入。舍棄非零部分,同時不會非零舍棄部分相鄰的一位數字加一,采取截取行為。 - ROUND_CEILING
向正無窮的方向舍入。如果為正數,舍入結果同ROUND_UP一致;如果為負數,舍入結果同ROUND_DOWN一致。注意:此模式不會減少數值大小。 - ROUND_FLOOR
向負無窮的方向舍入。如果為正數,舍入結果同ROUND_DOWN一致;如果為負數,舍入結果同ROUND_UP一致。注意:此模式不會增加數值大小。 - ROUND_HALF_UP
向“最接近”的數字舍入,如果與兩個相鄰數字的距離相等,則為向上舍入的舍入模式。如果舍棄部分>= 0.5,則舍入行為與ROUND_UP相同;否則舍入行為與ROUND_DOWN相同。這種模式也就是我們常說的我們的“四舍五入”。 - ROUND_HALF_DOWN
向“最接近”的數字舍入,如果與兩個相鄰數字的距離相等,則為向下舍入的舍入模式。如果舍棄部分> 0.5,則舍入行為與ROUND_UP相同;否則舍入行為與ROUND_DOWN相同。這種模式也就是我們常說的我們的“五舍六入”。 - ROUND_HALF_EVEN
向“最接近”的數字舍入,如果與兩個相鄰數字的距離相等,則相鄰的偶數舍入。如果舍棄部分左邊的數字奇數,則舍入行為與 ROUND_HALF_UP 相同;如果為偶數,則舍入行為與 ROUND_HALF_DOWN 相同。注意:在重復進行一系列計算時,此舍入模式可以將累加錯誤減到最小。此舍入模式也稱為“銀行家舍入法”,主要在美國使用。四舍六入,五分兩種情況,如果前一位為奇數,則入位,否則舍去。 - ROUND_UNNECESSARY
斷言請求的操作具有精確的結果,因此不需要舍入。如果對獲得精確結果的操作指定此舍入模式,則拋出ArithmeticException。
下面,舉個例子說明一下不同舍入模式下的數值計算結果,保留一位小數:
1551360361(1).jpg
為了使用方便可以根據需要寫個util
public class BigDecimalUtl {
private BigDecimalUtl() {
}
//加法
public static BigDecimal add(double d1, double d2) {
BigDecimal b1 = new BigDecimal(Double.toString(d1));
BigDecimal b2 = new BigDecimal(Double.toString(d2));
return b1.add(b2);
}
//減法
public static BigDecimal sub(double d1, double d2) {
BigDecimal b1 = new BigDecimal(Double.toString(d1));
BigDecimal b2 = new BigDecimal(Double.toString(d2));
return b1.subtract(b2);
}
//乘法
public static BigDecimal multiply(double d1, double d2) {
BigDecimal b1 = new BigDecimal(Double.toString(d1));
BigDecimal b2 = new BigDecimal(Double.toString(d2));
return b1.multiply(b2);
}
//除法
public static BigDecimal divide(double d1, double d2) {
BigDecimal b1 = new BigDecimal(Double.toString(d1));
BigDecimal b2 = new BigDecimal(Double.toString(d2));
//除不盡時有多種重載方法,此處選擇保留兩位小數,四舍五入
return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);
}
}