BigDecimal淺析

爲何使用BigDecimal

首先看一個例子:
public class DoubleTest {
public static void main(String[] args) {
    System.out.println(0.1 + 0.2);
    }
}
輸出的結果:0.30000000000000004(咱們的預期是0.3)
其實float和double類型的主要設計目標是爲了科學計算和工程計算。他們執行二進制浮點運算,這是爲了在廣域數值範圍上提供較爲精確的快速近似計算而精心設計的。然而,它們沒有提供徹底精確的結果,因此不該該被用於要求精確結果的場合。全部就出現了BigDecimal。

jdk文檔BigDecimal的描述:
不可變的、任意精度的有符號十進制數。BigDecimal 由任意精度的整數非標度值和32位的整數標度(scale)組成。BigDecimal 表示的數值是 (unscaledValue × 10-scale)。

BigDecial構造方法

下面是經常使用的四種構造方法:
BigDecimal(int)       
BigDecimal(double) 
BigDecimal(long)   
BigDecimal(String)

public static void main(String[] args) {
    double a = 123.11;
    BigDecimal bigDecimal = new BigDecimal(a);
    System.out.println(bigDecimal);
}
輸出結果:123.1099999999999994315658113919198513031005859375
public static void main(String[] args) {
    String a = "123.11";
    BigDecimal bigDecimal = new BigDecimal(a);
    System.out.println(bigDecimal);
}
輸出結果:123.11
public static void main(String[] args) {
    double a = 123.11;
    System.out.println(BigDecimal.valueOf(a));
}
輸出結果:123.11

BigDecimal(double)這個構造方法爲何會輸出不是咱們預期的值:
查看源碼的註釋得知: new BigDecimal(0.1)所建立的BigDecimal實際上等於 0.1000000000000000055511151231257827021181583404541015625,這是由於0.1沒法準確地表示爲 double(或者不能表示爲任何有限長度的二進制小數),因此建議使用BigDecimal(String)構造方法。

valueOf其實也調用BigDecimal(String)構造方法
public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO.  This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
}

BigDecimal基本運算

add(BigDecimal)        BigDecimal對象中的值相加
subtract(BigDecimal) BigDecimal對象中的值相減 
multiply(BigDecimal)  BigDecimal對象中的值相乘 
divide(BigDecimal)     BigDecimal對象中的值相除
注意:+-* /運算返回的是新的BigDecimal對象
比較大小: compareTo()  
-一、0、1,即左邊比右邊數大,返回1,相等返回0,比右邊小返回-1。注意不能使用equals方法來比較大小。

//add(BigDecimal)源碼
public BigDecimal add(BigDecimal augend) {
    if (this.intCompact != INFLATED) {
        if ((augend.intCompact != INFLATED)) {
            return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
        } else {
            return add(this.intCompact, this.scale, augend.intVal, augend.scale);
        }
    } else {
        if ((augend.intCompact != INFLATED)) {
            return add(augend.intCompact, augend.scale, this.intVal, this.scale);
        } else {
            return add(this.intVal, this.scale, augend.intVal, augend.scale);
        }
    }
}
//add
private static BigDecimal add(final long xs, int scale1, BigInteger snd, int scale2) {
    int rscale = scale1;
    long sdiff = (long)rscale - scale2;
    boolean sameSigns =  (Long.signum(xs) == snd.signum);
    BigInteger sum;
    if (sdiff < 0) {
        int raise = checkScale(xs,-sdiff);
        rscale = scale2;
        long scaledX = longMultiplyPowerTen(xs, raise);
        if (scaledX == INFLATED) {
            sum = snd.add(bigMultiplyPowerTen(xs,raise));
        } else {
            sum = snd.add(scaledX);
        }
    } else { //if (sdiff > 0) {
        int raise = checkScale(snd,sdiff);
        snd = bigMultiplyPowerTen(snd,raise);
        sum = snd.add(xs);
    }
    return (sameSigns) ?
        //新對象
        new BigDecimal(sum, INFLATED, rscale, 0) :
        //valueOf()返回的也是新對象
        valueOf(sum, rscale, 0);
}

BigDecimal格式化

若是咱們要將String轉換爲BigDecimal,而後保留兩位小數。java

DecimalFormat format = new DecimalFormat("0.00");
System.out.println(format.format(new BigDecimal(str)));

//若是四捨五入呢
String str = "123.4444";
System.out.println(new BigDecimal(str).setScale(2,BigDecimal.ROUND_HALF_UP));

參數
直接刪除多餘的小數位,如2.35會變成2.3 setScale(1,BigDecimal.ROUND_DOWN) 

進位處理,2.35變成2.4  setScale(1,BigDecimal.ROUND_UP) 

四捨五入,2.35變成2.4  setScale(1,BigDecimal.ROUND_HALF_UP) 

四捨五入,2.35變成2.3,若是是5則向下舍setScaler(1,BigDecimal.ROUND_HALF_DOWN)

BigDecimal使用時注意點

(1)儘可能使用參數類型爲String的構造函數。app

(2) BigDecimal都是不可變的,每次計算會產生新的對象,因此+-*/後保存值,如:a.dd(b)要寫成a = a.add(b)。ide

BigDecimal 由任意精度的整數非標度值 和 32 位的整數標度 (scale) 組成理解

例如:-314和 3.1415
表示爲:-314 × 10-0 和13412 × 10-4
這裏用(非標度值 和 標度)表示分別爲:[-314, 0]和[13412, 4]

BigDecimal amount = new BigDecimal("314");
System.out.println(amount.signum());//正負
System.out.println(amount.scale()); //標度
System.out.println(amount.stripTrailingZeros());
System.out.println(amount.stripTrailingZeros().scale());//去零後的標度(注意是末尾的0)
//結果
1
0
314
0

BigDecimal amount = new BigDecimal("3.1415");
System.out.println(amount.signum());//正負
System.out.println(amount.scale()); //標度
System.out.println(amount.stripTrailingZeros());
System.out.println(amount.stripTrailingZeros().scale());//去零後的標度
//結果
1
4
3.1415
4
//你們嘗試理解判斷是不是整數這段代碼
private boolean isIntegerValue(BigDecimal bd) {  
  return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0;  
}
相關文章
相關標籤/搜索