java浮點數精度丟失,如何最小化數值誤差

IEEE 754是美國電氣電子工程師協會通過地一個標準,用於在計算機上表示浮點數。Java採用32位IEEE 754表示float型,64位IEEE 754表示doubl型。 IEEE 標準中,浮點數是將特定長度的連續字節的所有二進制位分割爲特定寬度的符號域,指數域和尾數域三個域,其中保存的值分別用於表示給定二進制浮點數中的符號,指數和尾數。這樣,通過尾數和可以調節的指數(所以稱爲"浮點")就可以表達給定的數值了,比如 123.45 用十進制科學計數法可以表達爲 1.2345 × 102 ,其中 1.2345 爲尾數,10 爲基數,爲指數。浮點數利用指數達到了浮動小數點的效果,從而可以靈活地表達更大範圍的實數。

具體的格式參見下面的圖例:


    float和double類型在java中執行地是二進制浮點運算,這是爲了在廣泛的數值範圍上提供較爲精確的快速近似技術安而精心設計的。然而,它們並沒有提供完全精確的結果,只要是超過精度能表示的範圍就會產生誤差。往往產生誤差不是 因爲數的大小,而是因爲數的精度。因此,產生的結果接近但不等於想要的結果。尤其在使用 float  double 作精確運算的時候要特別小心。

將實數轉換成浮點數

4.1  浮點數的規範化

同樣的數值可以有多種浮點數表達方式,比如上面例子中的 123.45 可以表達爲 12.345 × 101,0.12345 × 103 或者 1.2345 × 102。因爲這種多樣性,有必要對其加以規範化以達到統一表達的目標。規範的(Normalized)浮點數表達方式具有如下形式:

±d.dd...d × βe , (0 ≤ i < β)

其中 d.dd...d 即尾數,β 爲基數,e 爲指數。尾數中數字的個數稱爲精度,在本文中用 p 來表示。每個數字 d 介於 0 和基數之間,包括 0。小數點左側的數字不爲 0。

基於規範表達的浮點數對應的具體值可由下面的表達式計算而得:

±(d 0 + d 1β-1 + ... + p-1β-(p-1)e , (0 ≤ i < β)

對於十進制的浮點數,即基數 β 等於 10 的浮點數而言,上面的表達式非常容易理解,也很直白。計算機內部的數值表達是基於二進制的。從上面的表達式,我們可以知道,二進制數同樣可以有小數點,也同樣具有類似於十進制的表達方式。只是此時 β 等於 2,而每個數字 d 只能在 0 和 1 之間取值。比如二進制數 1001.101 相當於 1 × 2 3 + 0 × 22 + 0 × 21 + 1 × 20 + 1 × 2-1 + 0 × 2-2 + 1 × 2-3,對應於十進制的 9.625。其規範浮點數表達爲 1.001101 × 23

4.2  根據精度表示浮點數

以上面的9.625爲例,其規範浮點數表達爲 1.001101 × 23

因此按單精度格式表示爲:

1 10000010 00110100000000000000000

同理按雙精度格式表示爲:

1 10000000010 0011010000000000000000000000000000000000000000000000


可以考慮採用一些替代方案來實現。如通過 String 結合 BigDecimal 或 者通過使用 long 類型來轉換。


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