1. 程式人生 > >Java-從Double類型精度丟失認識BigDecimal

Java-從Double類型精度丟失認識BigDecimal

程序 .html .cn 可能 cal pub 解決 ret 相關

Java-從Double類型精度丟失認識BigDecimal

參考資料

  • https://www.jianshu.com/p/07e3eeb90f18
  • https://zh.wikipedia.org/wiki/IEEE_754
  • https://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html

Double類型丟失精度

public static void main(String[] args) {
    double value = 0.05 + 0.01;
    System.out.println(value);
    //0.060000000000000005
}

double 類型的 0.05 + 0.01 的結果我們猜想的應該是 0.06

但是程序的數據結果卻是大大的超出了我們的預料(我們稱這種現象為精度丟失),造成上面的這種情況的原因是小數在計算機中的存儲形式造成的。

精度丟失的原因不是本篇博客的主要內容,所以在這裏不再贅述,想要了解的可以查看,文章頂部的 IEEE 754 的規範。

double 類型存在著上面的這種問題,所以我們肯定是不能用它來進行特別精細的計算了,比如 科學研究金融 相關的業務邏輯。因為一旦數據不精確就可能造成比較嚴重的問題。

BigDecimal

既然出現了上面的這種問題,肯定是有解決辦法的,官方提供的解決辦法就是 BigDecimal 類。

與BigDecimal類似的還有BigInteger

類.

構造BitDecimal

技術分享圖片

具有char[]參數的構造方法我們一般是不會去主動調用的,因為當我們調用具有string參數的構造函數時,會間接地調用char[]參數的構造方法。

public BigDecimal(String val) {
    this(val.toCharArray(), 0, val.length());
}

在構建BigDecimal的時候推薦使用String參數的構造方法,因為double參數的構造方法會會出現一些問題。

public static void main(String[] args) {
    BigDecimal bigDecimal = new BigDecimal(0.06);
    System.out.println(bigDecimal);
    
    BigDecimal bigDecimalStr = new BigDecimal("0.06");
    System.out.println(bigDecimalStr);
}

輸出結果如下

0.059999999999999997779553950749686919152736663818359375
0.06

通過輸出結果我們發現,當通過double參數的構造方法的來創建BigDecimal對象的時候,輸出結果更加的讓人頭大了 ??。造成這樣的情況的原因是:

技術分享圖片

官方文檔提到了像 0.1、0.06 這樣的double類型的數,不是一個精確的數值,所以造成了上面的這種情況。而官方文檔也推薦了使用string參數的構造方法來構造 BigDecimal對象來避免這種問題。

強大的BigDecimal

public static void main(String[] args) {
    BigDecimal bd1 = new BigDecimal("0.5");
    BigDecimal bdResult = bd1.add(new BigDecimal("0.1"));
    System.out.println(bdResult.toString());
}

通過BigDecimal我們就可以避免掉,精度就是造成的數據錯誤達的問題了,然而BigDecimal不僅僅可以用來解決精度就是的問題,double類型可以做到的BigDecimal類型同樣可以做到(如:四則運算);而BigDecimal可以進行精度的舍入(保留兩位小數等)功能則是double 類型做不具備的。

四則運算

運算 方法
加法 bd1.add
減法 bd1.subtract
乘法 bd1.multiply
除法 bd1.divide
取模/取余數 bd1.remainder

比較運算

通過BigDecimal對象可以實現比較大小和判斷是否相等的功能。

public static void main(String[] args) {
    BigDecimal bd1 = new BigDecimal("0.5");
    System.out.println(bd1.compareTo(new BigDecimal("0.5")));
    System.out.println(bd1.compareTo(new BigDecimal("0.1")));
    System.out.println(bd1.compareTo(new BigDecimal("0.6")));
}
/*
輸出結果:
0
1
-1
*/

控制精度

對精度的控制體現在保留小數上,我們可以通過BigDecimal.scale()方法來對精度進行控制,比如四舍五入保留兩位小數等。

public static void main(String[] args) {
    //四舍五入保留兩位小數
    BigDecimal bigDecimal = new BigDecimal("2.555");
    bigDecimal = bigDecimal.setScale(2, RoundingMode.HALF_UP);
    System.out.println(bigDecimal.toString());
    //向上取整
    bigDecimal = bigDecimal.setScale(0, RoundingMode.CEILING);
    System.out.println(bigDecimal);
    //向下取整
    bigDecimal = new BigDecimal("2.1");
    bigDecimal = bigDecimal.setScale(0, RoundingMode.FLOOR);
    System.out.println(bigDecimal.toString());
}
//輸出結果
/**
2.56
3
2
*/

註意

BigDecimal是不可變的,所以每一次操作都會創建一個新的BigDecimal對象。

Java-從Double類型精度丟失認識BigDecimal