double轉bigDecimal精度問題

double轉bigDecimal精度問題 需要用到bigDecimal的字符串構造來轉

float的精度 : 2^23 7位

double的精度: 2^52 16位

十進制 轉 二進制 存在精度差

double g= 12.35;

BigDecimal bigG=new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); //期望得到12.4

System.out.println(“test G:”+bigG.doubleValue());

test G:12.3

原因:

定義double g= 12.35; 而在計算機中二進制表示可能這是樣:定義了一個g=12.34444444444444449,
new BigDecimal(g) g還是12.34444444444444449
new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); 得到12.3
正確的定義方式是使用字符串構造函數:
new BigDecimal(“12.35”).setScale(1, BigDecimal.ROUND_HALF_UP)

首先得從計算機本身去討論這個問題。我們知道,計算機並不能識別除了二進制數據以外的任何數據。無論我們使用何種編程語言,在何種編譯環境下工作,都要先 把源程序翻譯成二進制的機器碼後才能被計算機識別。以上面提到的情況爲例,我們源程序裏的2.4是十進制的,計算機不能直接識別,要先編譯成二進制。但問 題來了,2.4的二進制表示並非是精確的2.4,反而最爲接近的二進制表示是2.3999999999999999。原因在於浮點數由兩部分組成:指數和尾數,這點如果知道怎樣進行浮點數的二進制與十進制轉換,應該是不難理解的。如果在這個轉換的過程中,浮點數參與了計算,那麼轉換的過程就會變得不可預 知,並且變得不可逆。我們有理由相信,就是在這個過程中,發生了精度的丟失。而至於爲什麼有些浮點計算會得到準確的結果,應該也是碰巧那個計算的二進制與 十進制之間能夠準確轉換。而當輸出單個浮點型數據的時候,可以正確輸出,如

double d = 2.4;
System.out.println(d);

輸出的是2.4,而不是2.3999999999999999。也就是說,不進行浮點計算的時候,在十進制裏浮點數能正確顯示。這更印證了我以上的想法,即如果浮點數參與了計算,那麼浮點數二進制與十進制間的轉換過程就會變得不可預知,並且變得不可逆。

事實上,浮點數並不適合用於精確計算,而適合進行科學計算。這裏有一個小知識:既然float和double型用來表示帶有小數點的數,那爲什麼我們不稱 它們爲“小數”或者“實數”,要叫浮點數呢?因爲這些數都以科學計數法的形式存儲。當一個數如50.534,轉換成科學計數法的形式爲5.053e1,它 的小數點移動到了一個新的位置(即浮動了)。可見,浮點數本來就是用於科學計算的,用來進行精確計算實在太不合適了。

在《Effective Java》這本書中也提到這個原則,float和double只能用來做科學計算或者是工程計算,在商業計算中我們要用java.math.BigDecimal。使用BigDecimal並且一定要用String來夠造。

BigDecimal用哪個構造函數?
BigDecimal(double val)
BigDecimal(String val)
上面的API簡要描述相當的明確,而且通常情況下,上面的那一個使用起來要方便一些。我們可能想都不想就用上了,會有什麼問題呢?等到出了問題的時候,才發現參數是double的構造方法的詳細說明中有這麼一段:
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.
原來我們如果需要精確計算,非要用String來夠造BigDecimal不可!

簡單來說 精確計算 ,需要用到bigDeicmal的String 構造

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章