先說說為什麼會丟精度。
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
使用 equals()
方法進行等值比較出現問題的程式碼示例:
這是因為 equals()
方法不僅僅會比較值的大小(value)還會比較精度(scale),而 compareTo()
方法比較的時候會忽略精度。
1.0 的 scale 是 1,1 的 scale 是 0,因此 a.equals(b)
的結果是 false。