float數據類型在內存中的存儲形式介紹:
float在內存中的存儲遵循IEEE 754標準。在C/C++中,float類型佔4個字節即32位 , 這32位分成了3部分:
符號位:轉化成二進制後,第31位。 0代表正數,1代表負數
階碼:30-23位,轉化成規格化的二進制之後與127之和
尾數:22-0位
例如:13.625在內存中的存儲
首先將13.625轉化成二進制
整數部分除2取餘,直到商爲0停止 。最後讀數時,從最後一個餘數讀起,一直到最前面的一個餘數
所以整數部分13的二進制位 1101;
小數部分乘2取整,然後從前往後讀。
0.625*2 = 1.25 取整 1
0.25*2 = 0.5 取整 0
0.5*2 = 1 取整 1
所以小數部分的二進制 101
然後將 1101.101的小數點向左移至小數點前只有一個1,即左移3位 。(這裏爲二進制的特定表示方法,將整數部分劃爲1)
階碼就是3+127 = 130 即:1000 0010
符號位:0
尾數 :因爲小數點前必爲一,所以記錄小數點後面的數即可 101101
0100 0001 0101 1010 0000 0000 0000 0000
轉換成16進制後爲 41 5A 00 00
float/double類型數據在內存中中存儲格式
float/double類型數據在計算機是如何存儲的呢?
它們是ieee standard 754的存儲方式。 譬如float數,第一位是符號位,然後是8位指數位,然後是23位尾數;double雙精度格式爲8字節64位,由三個字段組成:52位小數f,11位偏置指數e,以及1位符號s,這些字段連續存儲在兩個32位字中。
存儲結構
類型 | 符號位 | 指數位 | 尾數位 |
---|---|---|---|
float | 0 | 0xff | 0x7fffff |
double | 0 | 0x7ff | 0xfffffffffffff |
注意
上面的存儲結構,由於字節太多,所以後面使用的十六進制表示,7代表3位,f代表4位
浮點類型
從存儲結構和算法上來講,double和float是一樣的,不一樣的地方僅僅是float是32位的,double是64位的,所以double能存儲更高的精度以及更大的數值。float表示的正數範圍是3.40282346638528859811704183484516925440e+38~1.401298464324817070923729583289916131280e-45,double的正數範圍是1.797693134862315708145274237317043567981e+308~4.940656458412465441765687928682213723651e-324
【注:個人認爲,從階碼上移位來算,整數部分的確可以表示這麼大的數據範圍,但是從卻受到後面尾數的限制,float的有效位數只有6-7位,double的有效位數爲15-16位(至於具體爲什麼是6-7位、15-16位看下面的總結部分),所以感覺上面表示這麼大的數據範圍實際上意義不大。】
-
可以看出來double的表示的範圍比float大,而且由於尾數增加,實際上,精確度也比float高,但這樣的優勢帶來的就是更低的計算效率,雖然個人覺得不論是float還是double計算效率都被整形完爆,所以能夠用整型操作,就儘量不要用浮點型。
-
另外一個主要注意的則是,我們可以看到這裏該數的表示範圍並不像整形那樣,直接可以表示0,而是是一個很大到一個很接近0的數值,所以很多情況下浮點數判斷是否爲0,不是直接跟0比較是不是相等,而是約定一個很小的數,如果小於這個數,就等於0.
實際在內存中的存儲順序
任何數據在內存中都是以二進制(0或1)順序存儲的,每一個1或0被稱爲1位,而在x86CPU上一個字節是8位。比如一個16位(2字節)的short int型變量的值是1000,那麼它的二進制表達就是:00000011 11101000。由於Intel CPU的架構是小尾端表示,它是按字節倒序存儲的,那麼就因該是這樣:11101000 00000011,這就是定點數1000在內存中的結構。 (如果這裏不明白爲什麼這樣,可以搜索大尾端小尾端概念)
目前C/C++編譯器標準都遵照IEEE制定的浮點數表示法來進行float,double運算。這種結構是一種科學計數法,用符號、指數和尾數來表示,底數定爲2——即把一個浮點數表示爲尾數乘以2的指數次方再添上符號。下面是具體的規格:
類型 | 符號位 | 階碼 | 尾數 | 長度 |
---|---|---|---|---|
float | 1 | 8 | 23 | 32 |
double | 1 | 11 | 52 | 64 |
實戰:十進制轉十六進制
下面以double38414.4爲例:
- 把整數部和小數部分開處理:整數部直接化十六進制:960E。
- 小數的處理: 0.4=0.5*0+0.25*1+0.125*1+0.0625*0+……
你會發現第二步根本是坑爹嘛,根本算不完,那麼該怎麼辦呢?加上前面整數的精度960E,你只需要算夠53位就行了(最高位的1不寫入內存)。手工算到53位是:38414.4(10)=1001011000001110.011001100110011001100110011001100110011(2)
科學記數法:1.001……乘以2的15次方。
(注意指數是15,這裏的指數是2的次方,不是10的次方)
然後看階碼,一共11位,可以表示範圍是-1024 ~ 1023。因爲指數可以爲負,爲了便於計算,規定都先加上1023,在這裏,
15+1023=1038。二進制表示爲:100 00001110
符號位:正—— 0 !
合在一起(尾數二進制最高位的1不要):
01000000 11100010 11000001 11001101 01010101 01010101 01010101 01010101
假定機器爲小尾端,字節倒序存儲的十六進制數就是:
55 55 55 55 CD C1 E2 40
協議參考網址
IEEE Standard 754 for Binary Floating-Point Arithmetic
http://www.wikilib.com/wiki?title=IEEE_754&variant=zh-sg
--------------------------------------------------------------------------------------
總結:1 範圍
float和double的範圍是由指數的位數來決定的。
float的指數位有8位,而double的指數位有11位,分佈如下:
float:
1bit(符號位) |
8bits(指數位) |
23bits(尾數位) |
double:
1bit(符號位) |
11bits(指數位) |
52bits(尾數位) |
於是,float的指數範圍爲-127~+128,而double的指數範圍爲-1023~+1024,並且指數位是按補碼的形式來劃分的。其中負指數決定了浮點數所能表達的絕對值最小的非零數;而正指數決定了浮點數所能表達的絕對值最大的數,也即決定了浮點數的取值範圍。
float的範圍爲-2^128 ~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的範圍爲-2^1024 ~ +2^1024,也即-1.79E+308 ~ +1.79E+308。
2 精度
float和double的精度是由尾數的位數來決定的。浮點數在內存中是按科學計數法來存儲的,其整數部分始終是一個隱含着的“1”,由於它是不變的,故不能對精度造成影響。
float:2^23 = 8388608,一共七位,這意味着最多能有7位有效數字,但絕對能保證的爲6位,也即float的精度爲6~7位有效數字;
【也可以這麼算,0.000001的精度爲10^(-6), 2^20 = 1048576; 1/1048576的精度高於10^(-6), 但是23位不夠表示10^(-7),所以爲有效位數爲6-7位;】
double:2^52 = 4503599627370496,一共16位,同理,double的精度爲15~16位。
所以,float數據類型受到有限的有效位數限制,不能表示精度高的浮點型數據;