java float double精度為什麼會丟失?淺談java的浮點數精度問題 【轉】

來源:互聯網
上載者:User

標籤:

由於對float或double 的使用不當,可能會出現精度丟失的問題。問題大概情況可以通過如下代碼理解:

[java] view plaincopyprint?
  1. public class FloatDoubleTest {  
  2. public static void main(String[] args) {  
  3. float f = 20014999;  
  4. double d = f;  
  5. double d2 = 20014999;  
  6. System.out.println("f=" + f);  
  7. System.out.println("d=" + d);  
  8. System.out.println("d2=" + d2);  
  9. }  
  10. }  

public class FloatDoubleTest {public static void main(String[] args) {float f = 20014999;double d = f;double d2 = 20014999;System.out.println("f=" + f);System.out.println("d=" + d);System.out.println("d2=" + d2);}}

 

得到的結果如下:

f=2.0015E7

d=2.0015E7

d2=2.0014999E7

從輸出結果可以看出double 可以正確的表示20014999 ,而float 沒有辦法表示20014999 ,得到的只是一個近似值。這樣的結果很讓人訝異。20014999 這麼小的數字在float下沒辦法表示。於是帶著這個問 題,做了一次關於float和double學習,做個簡單分享,希望有助於大家對java 浮 點數的理解。

 

關於 java float double

Java 語言支援兩種基本的浮點類型: float 和 double 。java 的浮點類型都依據 IEEE 754 標準。IEEE 754 定義了32 位和 64 位元雙精確度兩種浮點二進位小數標準。

IEEE 754 用科學記號標記法以底數為 2 的小數來表示浮點數。32 位浮點數用 1 位表示數位符號,用 8 位來表示指數,用 23 位來表示尾數,即小數部分。作為有符號整數的指數可以有正負之分。小數部分用二進位(底數 2 )小數來表示。對於64 位元雙精確度浮點數,用 1 位表示數位符號,用 11 位表示指數,52 位表示尾數。如下兩個圖來表示:

float(32位):

double(64位):

都是分為三個部分:

(1) 一 個單獨的符號位s 直接編碼符號s 。

(2)k 位 的冪指數E ,移 碼錶示

(3)n 位 的小數,原碼錶示

那麼 20014999 為什麼用 float 沒有辦法正確表示?

結合float和double的表示方法,通過分析 20014999 的二進位表示就可以知道答案了。

以下程式可以得出 20014999 在 double 和 float 下的二進位表示方式。

[java] view plaincopyprint?
  1. public class FloatDoubleTest3 {  
  2. public static void main(String[] args) {  
  3. double d = 8;  
  4. long l = Double.doubleToLongBits(d);  
  5. System.out.println(Long.toBinaryString(l));  
  6. float f = 8;  
  7. int i = Float.floatToIntBits(f);  
  8. System.out.println(Integer.toBinaryString(i));  
  9. }  
  10. }  

public class FloatDoubleTest3 {public static void main(String[] args) {double d = 8;long l = Double.doubleToLongBits(d);System.out.println(Long.toBinaryString(l));float f = 8;int i = Float.floatToIntBits(f);System.out.println(Integer.toBinaryString(i));}}

 

輸出結果如下:

Double:100000101110011000101100111100101110000000000000000000000000000

Float:1001011100110001011001111001100

對於輸出結果分析如下。對雩都不 double 的二進位左邊補上符號位 0 剛好可以得到 64 位的位元。根據double的表 示法,分為符號數、冪指數和尾數三個部分如下:

0 10000010111 0011000101100111100101110000000000000000000000000000

對於 float 左邊補上符 號位 0 剛好可以得到 32 位的位元。 根據float的標記法, 也分為 符號數、冪指數和尾數三個部分如下 :

0 10010111 00110001011001111001100

綠色部分是符號位,紅色部分是冪指數,藍色部分是尾數。

對比可以得出:符號位都是 0 ,冪指數為移碼錶示,兩者剛好也相等。唯一不同的是尾數。

在 double 的尾數 為: 001100010110011110010111 0000000000000000000000000000 ,省略後面的零,至少需要24位才能正確表示 。

而在 float 下面尾數 為: 00110001011001111001100 ,共 23 位。

為什麼會這樣?原因很明顯,因為 float尾數 最多隻能表示 23 位,所以 24 位的 001100010110011110010111 在 float 下面經過四捨五入變成了 23 位的 00110001011001111001100 。所以 20014999 在 float 下面變成了 20015000 。 也就是說 20014999 雖然是在float的表示範圍之內,但 在 IEEE 754 的 float 標記法精度長度沒有辦法表示出 20014999 ,而只能通過四捨五入得到一個近似值。

 

總結:

浮點運算很少是精確的,只要是超過精度能表示的範圍就會產生誤差。往往產生誤差不是 因為數的大小,而是因為數的精度。因此,產生的結果接近但不等於想要的結果。尤其在使用 float 和 double 作精確運 算的時候要特別小心。 可以考慮採用一些替代方案來實現。如通過 String 結合 BigDecimal 或 者通過使用 long 類型來轉換。

java float double精度為什麼會丟失?淺談java的浮點數精度問題 【轉】

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.