標籤:
由於對float或double 的使用不當,可能會出現精度丟失的問題。問題大概情況可以通過如下代碼理解:
[java] view plaincopyprint?
- 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);
- }
- }
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?
- 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));
- }
- }
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的浮點數精度問題 【轉】