天天看點

float、double精度丢失

  都知道十進制整數轉換二進制( / 2),十進制小數轉換二進制( * 2 取 整),但小數有時乘不盡。

結論:

(1)整數永遠可以用二進制精确表示

(2)小數的二進制表示有時是不可能精确的

正如十進制不法表示1/3, 二進制也無法表示1/10;這也就解釋了為什麼浮點型減法出現了”減不盡”的精度丢失問題

而有時浮點數加減時小數點最後幾位出現了意外數字,

這是因為浮點數所在記憶體的表示形式,符合IEEE754标準。

float記憶體存儲結構

float記憶體存儲結構

4bytes      31           30           29----23       22----0         

         表示       實數符号位    指數符号位        指數位          有效數位

    其中符号位1表示正,0表示負。有效位數位24位,其中一位是實數符号位。



     将一個float型轉化為記憶體存儲格式的步驟為:

 (1)先将這個實數的絕對值化為二進制格式,注意實數的整數部分和小數部分的二進制方法在上面已經探讨過了。 
 (2)将這個二進制格式實數的小數點左移或右移n位,直到小數點移動到第一個有效數字的右邊。 
 (3)從小數點右邊第一位開始數出二十三位數字放入第22到第0位。 
 (4)如果實數是正的,則在第31位放入“0”,否則放入“1”。 
 (5)如果n 是左移得到的,說明指數是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”。 
 (6)如果n是左移得到的,則将n減去1後化為二進制,并在左邊加“0”補足七位,放入第29到第23位。如果n是右移得到的或n=0,則将n化為二進制後在左邊加“0”補足七位,再各位求反,再放入第29到第23位。



      舉例說明: 11.9的記憶體存儲格式

   (1) 将11.9化為二進制後大約是" 1011. 1110011001100110011001100..."。

   (2) 将小數點左移三位到第一個有效位右側: "1. 011 11100110011001100110 "。 保證有效位數24位,右側多餘的截取(誤差在這裡産生了 )。

   (3) 這已經有了二十四位有效數字,将最左邊一位“1”去掉,得到“ 011 11100110011001100110 ”共23bit。将它放入float存儲結構的第22到第0位。

   (4) 因為11.9是正數,是以在第31位實數符号位放入“0”。

   (5) 由于我們把小數點左移,是以在第30位指數符号位放入“1”。

   (6) 因為我們是把小數點左移3位,是以将3減去1得2,化為二進制,并補足7位得到0000010,放入第29到第23位。



       最後表示11.9為: 0 1 0000010 011 11100110011001100110      

可檢驗:

float f = 11.9f;
int i = Float.floatToIntBits(f);
System.out.println(Integer.toBinaryString(i));      

手動練習一個。

float、double精度丢失
float、double精度丢失