c語言浮點數(float double)內存存儲方式------移位存儲

一、概述

C語言中,對於浮點類型的數據採用單精度類型(float)和雙精度類型(double)來存儲,float數據佔用32bit,double數據佔用64bit, float遵從的是IEEE R32.24,而double 遵從的是R64.53。
Float類型變量佔4個字節,也就是32bit。單精度浮點值格式:1位符號位,8位指數,23位小數。
float變量的內存模型
具體如下:
二進制浮點數可以表示成:Float=(-1)s x M x2e
1、 符號位(sign):0代表正,1代表負。
2、 指數位(exponent):用於存儲科學計數法中的指數數據,並且採用移位存儲。
指數E分成三種情況:
(a)E不全爲0或不全爲1。指數E的計算值減去127,得到真實值,再將有效數字M前加上第一位的1。
(b)E全爲0。浮點數的指數E等於1-127(或者1-1023),有效數字M不再加上第一位的1,而是還原爲0.xxxxxx的小數。這樣做是爲了表示±0,以及接近於0的很小的數字。
(c)E全爲1。這時,如果有效數字M全爲0,表示±無窮大(正負取決於符號位s);如果有效數字M不全爲0,表示這個數不是一個數。
3、 尾數部分(mantissa):位數部分

二進制科學計數法第一位都是1,可以將小數點前面的1省略,所以23bit的位數部分可以表示精度爲24bit。

二、轉換示例

下面討論一下十進制數與二進制數的轉換方法:

十進制:0.15625轉換爲二進制表示的過程如下:
整數位:0
小數:0.15625
將小數轉換位二進制的方法就是乘二,取整數部分依次從左往右放在小數點後,直至小數點後爲0。
0.15625x2=0.31250  取0
0.3125x2=0.6250 取0
0.625x2=1.250 取1
0.25x2=0.5 取0
0.5x2=1 取0
(0.00101)2=1.01*2^(-3)
符號位:0,表示位正數
指數位:-3+127=124 表示爲二進制數就是01111100
尾數部分:1.01-1=0.01,後面不夠23位的補0也就是補21個零
即0.15625表示爲內存中二進制數的形式爲:
0  01111100  01000000000000000000000

0  01111100  01000000000000000000000
表示的數轉換爲十進制表示過程爲:
符號位:0,表示位正
指數位:01111100表示的十進制爲:64+32+16+8+4=124再減127=-3
尾數部分:0.01 加1表示:1+0*2^(-1)+1*2^(-2)=1.25
原數爲:2^(-3)*1.25=0.15625

三、基於轉換誤差的思考

最近偶然看到一段程序,,發現了int與float轉換時存在的問題,於是查詢了相關的數據:

#include<stdio.h>
int main(){
	int a=9;
	float *p;
	p=&a;
	printf("a數值爲:%d\n",a);
	printf("float形式爲:%f\n",*p);
	*p=9.0;
	printf("a數值爲:%d\n",a);
	printf("float形式爲:%f\n",*p);
	return 0;
}

結果令我很是不解:

a數值爲:9
float形式爲:0.000000
a數值爲:1091567616
float形式爲:9.000000

爲什麼0x00000009還原成浮點數就成了0.000000?
首先,將0x00000009拆分,因爲是正數所以得到第一位符號位s=0,後面8位的指數E=00000000,最後23位的有效數字M=000 0000 0000 0000 0000 1001。
由於指數E全爲0,所以符合第二種情況。因此,浮點數就寫成:
  (-1)0×0.00000000000000000001001×2(-126)=1.001×2(-146)
顯然是一個很小的接近於0的正數,所以用十進制小數表示就是0.000000。
浮點數9.0,怎麼用二進制表示?怎麼還原成十進制呢?
首先,浮點數9.0等於二進制的1001.0,即1.001×2^3。
那麼,第一位的符號位s=0,有效數字M等於001後面再加20個0,湊滿23位,指數E等於3+127=130,即10000010。
所以,寫成二進制形式,應該是S+E+M,即0 10000010 001 0000 0000 0000 0000 0000。這個32位的二進制數,還原成十進制,是1091567616。

四、爲什麼float有些時候表達的int數據不準確

因爲int與float同樣佔4個字節,float表示的範圍又比int大並且還包含很多小數,那int的每個值都能被float表示就是不可能的事情了。
尾數決定了浮點數的精度,尾數只有23位,加上省略的那位就是24位。如果一個int類型的值小於224,那麼float是完全可以表示的。如果int類型大於224就不一定能表示了。假如一個int數值的二進制表示形式是1000000000000000000000001,表示成指數形式是1.000000000000000000000001*224,對應的float的類型尾數位是000000000000000000000001一共24位,這樣就完全超出了float最多容納23位尾數的能力。所以就不能正確表達這個int值了。由此也可以得出不能被float準確表達的最小int值是224+1。我們再將1000000000000000000000001的值加1,變成了1000000000000000000000010,這樣變換爲指數形式可以看出尾數又變爲了23位。
也就是說25位的二進制整數最後一位是0才能被float準確表示,每2個數就有一個不能被準確表示。如果是26位的二進制整數最後兩位都是0纔可以被float準確表達,每4個數就有3個不能被準確表示,
即,如果是n(n>23且n爲整數)位二進制數,最後n-23-1位全爲0是纔可以被準確表示,每2(n-23-1)個數就有2(n-23)-1位不能被準確描述。

參考教程:
c變量|菜鳥教程
c語言單精度和雙精度存儲方式以及移位存儲的優點
浮點數的祕密

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章