原文出處:https://zhuanlan.zhihu.com/p/84453627?from_voters_page=true
ok,這里先說明一下,假設是在32位的機器上,int是32位。而float使用的是IEEE 754標准的單精度浮點數格式也是占用32位。
這時候float和int都是占用32位,占用同樣的空間,但float范圍是更大的,那我們為啥還要int呢?為啥不節省空間,只用float?我們來一探究竟!他們在計算機的大腦里是如何記憶的?
1、int對32個坑是如何使用的?
(實名吐槽知乎,居然沒有表格。。。)
int類型的使用方法,大多學過計算機的,應該都是非常清楚的。二進制存儲即可。
例1:請寫出165(10進制)使用32位int型存儲在計算機中的形式。
10進制轉換為2進制,我個人喜歡先轉為16進制,再寫成2進制。
如下:
那么165在32位int型中是這樣存儲的(中間的0,我省略了):
165在計算機中的int存儲
非常簡單明了,好理解。把10進制轉換2進制,直接存進去就ok,前面空位補0。
例2:請寫出-165(10進制)使用32位int型存儲在計算機中的形式。
這是一個負數,按照慣例int型首位為符號位。0表示在正數,1表示負數。
如下:
但在計算機中,負數存儲的是補碼,不是原碼。
他們之間按照如下轉換:
原碼:1000 0000 1010 0101
反碼:1111 1111 0101 1010 (除了符號位,其它取反)
補碼:1111 1111 0101 1011 (在反碼的基礎上加1即可)
那么-165在32位int型中是這樣存儲的(中間的1,我省略了):
-165在計算機中的int存儲
比正數復雜了一點,但是還是可以很容易算出來的。
問題來了?為啥負數要用補碼?這不是挑事嗎?原碼不好嗎?
原因之一在於,我們計算: ,在計算機中存儲的是二進制,
如果使用原碼進行計算,需要單獨把符號位拿出來,再做減法運算,而把符號位區分出來是需要額外的硬件電路支撐的,這很不方便。
如果使用補碼,如下所示(這里按照16位進行舉例):
0000 0000 1010 0101 + 1111 1111 0101 1011=0000 0000 0000 0000;
使用補碼參與運算后,無需再管符號位,可以讓符號位直接參與運算。這就是使用補碼的最大的好處。到這里大家有沒有發現,int型的這種存儲方案是沒有考慮小數的,所以這是整型的。關於int型的存儲,不再贅述,整體來說還是清晰明了的一種方案。
2、float對32個坑是如何使用的?
同樣也是占用32個坑,float型的范圍比int就大很多,而且還能表示小數,那么它到底是如何利用這32個坑的呢?
例3:請寫出165.25(10進制)使用float型存儲在計算機中的形式。
同樣我們還是先轉換為2進制: 1010 0101 . 0100
那么如何把上面的二進制小數存到32個坑里呢?
在填坑之前,我們先要規范二進制小數的表示形式,就和我們的科學計數法一樣的道理。
(就像 要寫成
這個樣子,把所有的小數換成統一的格式)
IEEE754標准做了這樣的規定:當尾數(小數)不為0時,尾數域的最高有效位為1,這稱為浮點數的規格化。
例如:
規格化后的二進制小數,有了統一的規格,可以發現這樣規格化之后,我們只需要存儲一個尾數(即小數部分,整數部分恆為1)和指數部分。
IEEE754標准把float型的32個坑做了如下划分:
其中包含了1位符號位S,8位階碼E和23位尾數M。
,要存儲這個二進制小數;
首先符號位S,0表示正數,1表示負數。S=0;
再寫出尾數M,即:M=0100 1010 1000 0000 0000 000;
然后算出階碼E,這里指數為:e=7=0000 0111,根據標准要求,E=e+127;
即:E=7+127=134=1000 0110;
那么把這三個數都填進坑里,就ok啦。
165.25在計算機中的float儲存
這個計算過程稍微復雜點,但也還可以手算出來。
但是問題又來了:
1、浮點數的表示范圍有多大?
2、為什么要用指數加上127,才是階碼E,而不是直接用指數存進去?
3、這個過程可以看出float有效位是尾數M加1也就是24位,階碼E只是我們規范科學計數法記錄指數的,但int有效位是32位,float實際有效位比int少,那么在相互轉換的過程中會出現什么問題?
我依次解釋這3個問題:
1、浮點數的表示范圍有多大?
float型定義的正無窮大
float型定義的負無窮小
可以得出當E= 1111 1111時,指數為255-127=128,但這並不是表示這個數是: ,在IEEE754把這種情況定義為無窮大,此時尾數必須全部為0,不能有其他值,否則就認為無效數字。
那么除了無窮大這個特殊的、人為定義的情況,float型能表示的最大的正整數是多少?最小的負整數是多少?當E= 1111 1111時,是IEEE754定義的特殊值即為無窮大,那么除此之外的最大值就是:E= 1111 1110,M也取最大值,即得到如下結果:
float型能存儲的最大正整數
此時階碼E為254,指數即為e=254-127=127。這個數即為:
;
對於尾數我們可以換一個寫法:
1.1111 1111 1111 1111 1111 111=10-0.0000 0000 0000 0000 0000 001
這樣尾數可以寫成: ;
那么float能夠表示的最大正整數就是: ,即為
。
那么float能夠表示的最大負整數就是: 。
2、為什么要用指數加上127,才是階碼E,而不是直接用指數存進去?
這就很容易說明了,我們舉個例子:
例4:請寫出0.75(10進制)使用float型存儲在計算機中的形式。
寫成二進制: 。再寫成規划化的計數法:
;
發現問題了沒有?這次的指數是個負數啦,而我們希望存儲到機器里的階碼永遠都是正值,因為我們不希望再浪費一個坑去保存階碼的正負號,於是乎,干脆把指數加上127,而指數能取到的最小值就是-127,這樣就可以保證階碼E永遠都是正數啦,我們就不用再考慮指數正負號的問題了。
E=-1+127=126=0111 1110;
M=1000 0000 0000 0000 0000 000;
0.75在計算機中的float儲存
3、這個過程可以看出float有效位是尾數M加1也就是24位,階碼E只是我們用於規范科學計數法記錄指數的,但int有效位是32位,float實際有效位比int少,那么在相互轉換的過程中會出現什么問題?
通過問題1知道,float型的表示范圍是比int大很多的,但有效位確實只有24位。既然float范圍大,那么所有的int型都是可以轉換為float型的,這是不會產生溢出報錯的。但因為int型有效位是32位,是比float型的24位大的,是有可能發生舍入的,即當一個int型數字,轉成float型后,可能就不再是原本數字了,損失了一定的精度。
例如2進制int型正數:0111 1111 1111 1111 1111 1111 1111 1111;
寫成科學計數法即為:
小數點后面有30個1,但是我們知道float種尾數M只有23個坑。
則轉化為float型后,階碼E=30+127=157=1001 1101
可以發現,我們對原int型中存儲的數字只保留了小數點后23個1,而后面7個,直接忽視了,這就是發生了舍入。
如果既要范圍大,還要保留精度,那就上雙精度浮點型double,double型的存儲規則和float型是十分類似的。double型有64個坑位,包括了1個符號位S,11個階碼位E和52個尾數位M。所以double的有效位有53位,可以完整保留int。
ok,同樣是占32個坑,那憑啥你float就比int的范圍更大?因為float型雖然范圍大,但是精度不足啊!所以各有千秋哦。