Java BigDecimal和double

BigDecimal類java

對於不須要任何準確計算精度的數字能夠直接使用float或double,可是若是須要精確計算的結果,則必須使用BigDecimal類,並且使用BigDecimal類也能夠進行大數的操做。BigDecimal類的經常使用方法如表11-15所示。ide

表11-15 BigDecimal類的經常使用方法函數

序號spa

   code

類型blog

   ip

1ci

public BigDecimal(double val)字符串

構造get

將double表示形式轉換

爲BigDecimal

2

public BigDecimal(int val)

構造

將int表示形式轉換爲

BigDecimal

3

public BigDecimal(String val)

構造

將字符串表示

形式轉換爲BigDecimal

4

public BigDecimal add(BigDecimal augend)

普通

加法

5

public BigDecimal subtract(BigDecimal
subtrahend)

普通

減法

6

public BigDecimal multiply(BigDecimal
multiplicand)

普通

乘法

7

public BigDecimal divide(BigDecimal
divisor)

普通

除法

範例:進行四捨五入的四則運算

    package org.lxh.demo11.numberdemo;
    import java.math.BigDecimal;
    class MyMath {
        public static double add(double d1, double d2)
    {        // 進行加法運算
             BigDecimal b1 = new BigDecimal(d1);
             BigDecimal b2 = new BigDecimal(d2);
            return b1.add(b2).doubleValue();
         }
        public static double sub(double d1, double d2)
    {        // 進行減法運算
             BigDecimal b1 = new BigDecimal(d1);
             BigDecimal b2 = new BigDecimal(d2);
            return b1.subtract(b2).doubleValue();
         }
        public static double mul(double d1, double d2)
    {        // 進行乘法運算
             BigDecimal b1 = new BigDecimal(d1);
             BigDecimal b2 = new BigDecimal(d2);
            return b1.multiply(b2).doubleValue();
         }
        public static double div(double d1,
    double d2,int len) {// 進行除法運算
             BigDecimal b1 = new BigDecimal(d1);
             BigDecimal b2 = new BigDecimal(d2);
            return b1.divide(b2,len,BigDecimal.
    ROUND_HALF_UP).doubleValue();
         }
        public static double round(double d,
    int len) {     // 進行四捨五入
    操做
             BigDecimal b1 = new BigDecimal(d);
             BigDecimal b2 = new BigDecimal(1);
            // 任何一個數字除以1都是原數字
            // ROUND_HALF_UP是BigDecimal的一個常量,
    表示進行四捨五入的操做
            return b1.divide(b2, len,BigDecimal.
    ROUND_HALF_UP).doubleValue();
         }
    }
    public class BigDecimalDemo01 {
        public static void main(String[] args) {
             System.out.println("加法運算:" +
    MyMath.round(MyMath.add(10.345,
    3.333), 1));
             System.out.println("乘法運算:" +
    MyMath.round(MyMath.mul(10.345,
    3.333), 3));
             System.out.println("除法運算:" +
    MyMath.div(10.345, 3.333, 3));
             System.out.println("減法運算:" +
    MyMath.round(MyMath.sub(10.345,
    3.333), 3));
         }
    }

 

BigDecimal是Java中用來表示任意精確浮點數運算的類,在BigDecimal中,使用unscaledValue × 10-scale來表示一個浮點數。其中,unscaledValue是一個BigInteger,scale是一個int。從這個表示方法來看,BigDecimal只能標識有限小數,不過能夠表示的數據範圍遠遠大於double,在實際應用中基本足夠了。

下面提一下兩個精度問題:
 
問題一:BigDecimal的精度問題(StackOverflow上有個傢伙問了相關的問題
System.out.println(new BigDecimal(0.1).toString()); // 0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal("0.1").toString()); // 0.1
System.out.println(new BigDecimal(
Double.toString(0.1000000000000000055511151231257827021181583404541015625)).toString());// 0.1
System.out.println(new BigDecimal(Double.toString(0.1)).toString()); // 0.1

 

分析一下上面代碼的問題(註釋的內容表示此語句的輸出)

第一行:事實上,因爲二進制沒法精確地表示十進制小數0.1,可是編譯器讀到字符串"0.1"以後,必須把它轉成8個字節的double值,所以,編譯器只能用一個最接近的值來代替0.1了,即0.1000000000000000055511151231257827021181583404541015625。所以,在運行時,傳給BigDecimal構造函數的真正的數值是0.1000000000000000055511151231257827021181583404541015625。
第二行:BigDecimal可以正確地把字符串轉化成真正精確的浮點數。
第三行:問題在於Double.toString會使用必定的精度來四捨五入double,而後再輸出。會。Double.toString(0.1000000000000000055511151231257827021181583404541015625)輸出的事實上是"0.1",所以生成的BigDecimal表示的數也是0.1。
第四行:基於前面的分析,事實上這一行代碼等價於第三行
 
結論:
1.若是你但願BigDecimal可以精確地表示你但願的數值,那麼必定要使用字符串來表示小數,並傳遞給BigDecimal的構造函數。
2.若是你使用Double.toString來把double轉化字符串,而後調用BigDecimal(String),這個也是不靠譜的,它不必定按你的想法工做。
3.若是你不是很在意是否徹底精確地表示,而且使用了BigDecimal(double),那麼要注意double自己的特例,double的規範自己定義了幾個特殊的double值(Infinite,-Infinite,NaN),不要把這些值傳給BigDecimal,不然會拋出異常。
 
問題二:把double強制轉化成int,難道不是扔掉小數部分嗎?
int x=(int)1023.99999999999999; // x=1024爲何?

緣由仍是在於二進制沒法精確地表示某些十進制小數,所以1023.99999999999999在編譯以後的double值變成了1024。

因此, 把double強制轉化成int確實是扔掉小數部分,可是你寫在代碼中的值,並不必定是編譯器生成的真正的double值
驗證代碼:
double d = 1023.99999999999999;
int x = (int) d;
System.out.println(new BigDecimal(d).toString()); // 1024
System.out.println(Long.toHexString(Double.doubleToRawLongBits(d))); // 4090000000000000
System.out.println(x); // 1024

前面提過BigDecimal能夠精確地把double表示出來還記得吧。

咱們也能夠直接打印出d的二進制形式,根據IEEE 754的規定,咱們能夠算出0x4090000000000000=(1024)。
相關文章
相關標籤/搜索