BigDecimal為什麼能保證精度不丟失?

zhengbiyu發表於2024-06-08

先說說為什麼會丟精度。

public static void main(String[] args) {
        //正常 3.3
        System.out.println("加法結果:"+(1.1+2.2));
        //正常 -7.9
        System.out.println("減法結果:"+(2.2-10.1));
        //正常 2.42
        System.out.println("乘法結果:"+(1.1*2.2));
        //正常 0.44
        System.out.println("除法結果:"+(4.4/10));
}

結果:

為什麼會丟失精度?

在於我們的計算機是二進位制的。十進位制小數沒有辦法是用二進位制進行精確表示

浮點數的值實際上是由一個特定的數學公式計算得到的。

// 0.2 轉換為二進位制數的過程為,不斷乘以 2,直到不存在小數為止,
// 在這個計算過程中,得到的整數部分從上到下排列就是二進位制的結果。
0.2 * 2 = 0.4 -> 0
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
0.2 * 2 = 0.4 -> 0(發生迴圈)
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
...

所以二進位制表示為:0.001100110011001100110011001100110011001100110011001101...

迴圈根為:0011

但是十進位制的整數轉換為二進位制沒有問題,那麼把十進位制小數擴大N倍讓它在整數的維度上進行計算,並保留相應的精度資訊,這就是BigDecimal的做法。

BigDecimal

scale表示精度資訊,intCompact表示整數資訊。

因此,BigDecimal 表示的數值是 (intCompact × 10-scale)。

四種建構函式

BigDecimal(int)     //建立一個具有引數所指定整數值的物件。
BigDecimal(double)  //建立一個具有引數所指定雙精度值的物件。
BigDecimal(long)    //建立一個具有引數所指定長整數值的物件。
BigDecimal(String)  //建立一個具有引數所指定以字串表示的數值的物件。

這幾個都是常用的構造器,他們返回的物件都是BigDecimal物件。換而言之,將BigDecimal物件轉換為其他型別的物件,我們透過以下幾種。

toString()          //將BigDecimal物件的數值轉換成字串。
doubleValue()       //將BigDecimal物件中的值以雙精度數返回。
floatValue()        //將BigDecimal物件中的值以單精度數返回。
longValue()         //將BigDecimal物件中的值以長整數返回。
intValue()          //將BigDecimal物件中的值以整數返回。

這裡需要非常注意BigDecimal(double)的建構函式,也是會存在精度丟失的問題。

public static void main(String[] args) {
        BigDecimal intDecimal = new BigDecimal(10);
        BigDecimal doubleDecimal = new BigDecimal(4.3);
        BigDecimal longDecimal = new BigDecimal(10L);
        BigDecimal stringDecimal = new BigDecimal("4.3");
        System.out.println("intDecimal=" + intDecimal);
        System.out.println("doubleDecimal=" + doubleDecimal);
        System.out.println("longDecimal=" + longDecimal);
        System.out.println("stringDecimal=" + stringDecimal);
}

結果:

所以建立BigDecimal一定要使用下面兩種方式:

1、new BigDecimal(String)
2、BigDecimal.valueOf(double)

BigDecimal 等值比較問題

《阿里巴巴 Java 開發手冊》中提到:

BigDecimal為什麼能保證精度不丟失?

BigDecimal 使用 equals() 方法進行等值比較出現問題的程式碼示例:

BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("1.0");
System.out.println(a.equals(b));//false

這是因為 equals() 方法不僅僅會比較值的大小(value)還會比較精度(scale),而 compareTo() 方法比較的時候會忽略精度。

1.0 的 scale 是 1,1 的 scale 是 0,因此 a.equals(b) 的結果是 false。

相關文章