浮點數在內存中是如何存儲的?
我們知道,任何數據在計算機內存中都是用‘0\1’來存儲的,浮點數亦是如此。因此十進制浮點數在存儲時必定會轉換為二進制的浮點數。
浮點數的進制轉換
主要看看十進制轉二進制,整數部分和小數部分分開處理
-
整數部分:整數除以2,得到一個商和余數,得到的商繼續除以2並得到一個商和一個余數,繼續除以2操作直至商為0,上述操作得到一系列余數,從最后一個余數開始直至第一個余數,這一系列0\1即為轉換后的二進制數。
-
小數部分:乘以2,然后取出整數部分,將剩下的小數部分繼續乘以2,然后再取整數部分,一直取到小數部分為零為止。如果永遠不為零,則按要求保留足夠位數的小數,最后一位做0舍1入。將取出的整數順序排列。
從以上轉換過程可以看出,並不是任何一個十進制小數都可以用二進制精確表示出來。一個在0到1之間的小數P可用如下形式表示:
從這個式子中我們也可看出二進制表示出的小數是分段的,這也是為什么在Java中浮點數很多時候並不是十分精確的表示十進制小數的根本原因。
float在內存中的表示
float在Java中是4字節(32位),取值范圍大約-3.4E+38F~3.4E
+38F。
為什么取值范圍是這個呢?
二進制在內存中使用二進制的科學計數法來存儲,因此分為階碼(即指數)和底數,由於也有正負之分,所以還有一位符號位。
最高位為符號位,接着8位階碼,剩下23位為底數。
值得注意的是,無論底數是什么值,我們都可以進行移位操作,使得底數=1.xxxxxx,這類似於十進制中的任意一個數轉換為用科學計數法表示時進行的移位操作。由於底數前的一個1是固定不變的,故將其省略(為了表示更大的范圍)。也就是說實際上底數是24位。
底數可以表示的最大值為1.1111111111111111111111111(共24個1),轉換為十進制的值約為1.999999(接近於2)。
底數的最小值為1.00000000000000(23個0),轉換為十進制為1。
故可知,底數取值在1~2之間。
階碼在這里用移碼表示,采用偏移值為127的移碼,為什么要這樣做?浮點數的階碼中,全0和全1被保留作特殊情況,所以實際只有254個值可用。
階碼必定是有負值的,那么如何最優雅地表示負值呢?為了讓計算機很容易比較階碼大小(以此來比較浮點數的大小),同時人也可以很輕松地辨別大小,因為移碼相當於實際值在數軸上平移了一定位數。
我覺得應該是為了平衡精度與范圍,並且讓表示的范圍盡可能大。
階碼用移碼表示后,8位二進制從1254以此表示為-126127。不僅去除了全0和全1的情況,還能讓人一眼看出兩個階碼的大小。
知道階碼和底數的范圍后,就可以得出float的取值范圍:
轉化為十進制為
-3.4*10^38~-1.2*10^(-38)
與1.2*10^(-38)~3.4*10^38