Java 中浮點數---------BigDecimal和double(初探)

爲何要使用 bigdecimal?html

借用《Effactive Java》這本書中的話,floatdouble類型的主要設計目標是爲了科學計算和工程計算。他們執行二進制浮點運算,這是爲了在廣域數值範圍上提供較爲精確的快速近似計算而精心設計的。然而,它們沒有提供徹底精確的結果,因此不該該被用於要求精確結果的場合。可是,商業計算每每要求結果精確,這時候BigDecimal就派上大用場啦。java

 

BigDecimal簡介函數

        BigDecimal 由任意精度的整數非標度值 32 位的整數標度 (scale) 組成。若是爲零或正數,則標度是小數點後的位數。若是爲負數,則將該數的非標度值乘以 10 的負scale 次冪。所以,BigDecimal表示的數值是(unscaledValue × 10-scale)源碼分析

 

構造方法spa

BigDecimal aString = new BigDecimal("1.22");設計

BigDecimal aDouble = new BigDecimal(1.22);code

BigDecimal bDouble = BigDecimal.valueOf(1.22);orm

System.out.println(aString);htm

System.out.println(aDouble);對象

System.out.println(bDouble);

Result:

1.22

1.2199999999999999733546474089962430298328399658203125

1.22

源碼分析:

BigDecimal.valueOf(1.22) 構造方法,實際上是調用 構造方法BigDecimal(String val)

 

 

Jdk中有說明:

1、參數類型爲double的構造方法的結果有必定的不可預知性。有人可能認爲在Java中寫入newBigDecimal(0.1)所建立的BigDecimal正好等於 0.1(非標度值 1,其標度爲 1),可是它實際上等於0.1000000000000000055511151231257827021181583404541015625。這是由於0.1沒法準確地表示爲 double(或者說對於該狀況,不能表示爲任何有限長度的二進制小數)。這樣,傳入到構造方法的值不會正好等於 0.1(雖然表面上等於該值)。

2、另外一方面,String 構造方法是徹底可預知的:寫入 newBigDecimal("0.1") 將建立一個 BigDecimal,它正好等於預期的 0.1。所以,比較而言,一般建議優先使用String構造方法。

3、當double必須用做BigDecimal的源時,請注意,此構造方法提供了一個準確轉換;它不提供與如下操做相同的結果:先使用Double.toString(double)方法,而後使用BigDecimal(String)構造方法,將double轉換爲String。要獲取該結果,請使用static valueOf(double)方法。

 

 

 

在數據量精度不是很大的狀況下,經過decimal輸出正確的結果。(保留三位小數)

BigDecimal aString = new BigDecimal("1.22");

BigDecimal aDouble = new BigDecimal(1.22);

BigDecimal aValue = BigDecimal.valueOf(1.22);

System.out.println(new DecimalFormat("0.000").format(aString));

System.out.println(new DecimalFormat("0.000").format(aDouble));

System.out.println(new DecimalFormat("0.000").format(aValue));

Result

1.220

1.220

1.220

緣由分析:DecimalFormat作用了本身的舍餘規則

 

 

 

總結

(1)商業計算使用BigDecimal

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

(3) BigDecimal都是不可變的(immutable)的,在進行每一步運算時,都會產生一個新的對象,因此在作加減乘除運算時千萬要保存操做後的值。

(4)咱們每每容易忽略JDK底層的一些實現細節,致使出現錯誤,須要多加註意。

 

 

問題一: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)。
 
問題二:BigDecimal如何比較大小?
    public static void main(String[] args) {
        BigDecimal a = BigDecimal.valueOf(1.01);
        BigDecimal b = BigDecimal.valueOf(1.00000001);
        if (a.compareTo(b) == 0) {
            System.out.println("a==b");
        } else if (a.compareTo(b) == 1) {
            System.out.println("a>b");
        } else if (a.compareTo(b) == -1) {
            System.out.println("a<b");
        }
    }

 

 

 

參考:

http://www.cnblogs.com/mingforyou/p/3344489.html

http://stackoverflow.com/questions/8073912/why-do-we-need-to-convert-the-double-into-a-string-before-we-can-convert-it-int

http://www.cnblogs.com/linjiqin/p/3413894.html

相關文章
相關標籤/搜索