天天看點

Java double和 float丢失精度問題

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

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位):

Java double和 float丢失精度問題

double(64位):

Java double和 float丢失精度問題

都是分為三個部分:

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

(2)k 位 的幂指數e ,移 碼表示 。

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

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

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

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

public class floatdoubletest3 {  

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 類型來轉換。

特别說明:尊重作者的勞動成果,轉載請注明出處哦~~~http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt357