在c/c++中,整數一般分為無符號數(unsigned char、unsigned short、unsigned int等)和有符號數(char、short、int、long等),在計算機中通過補碼來表示,那麼有童鞋會問了,不是有那什麼原碼、反碼之類的嗎?為什麼不用它們而偏偏用補碼呢?
一開始我也有這樣的困惑,於是通過各種查,各種看,算是理解了一點點,這裡不打算詳細解釋原碼和反碼,只舉幾個例子說明在計算機中為什麼不用它們表示十進位制數,而用補碼來表示。
我們能認識0、1、2、3、、、9等自然數,然而計算機卻不認識,計算機「笨」的只認識0和1,所以那些大神們就通過各種手段,將現實世界中的各種資訊都轉化為一連串由0和1組成的序列。然後計算機就通過不同的解讀方式,對這一系列的01串串根據具體的上下文解釋成不同的資訊。
我們都知道1 + (-1) = 0
,那麼如果用原碼、反碼來表示這個會怎樣呢?
原碼:
原碼 十進位制
0000 0001 1
+ 1000 0001 -1
----------------
1000 0010 -2
反碼:
反碼 十進位制
0000 0001 1
+ 1111 1110 -1
----------------
1111 1111 -0 // 需要通過轉換為原碼,才能直觀的看出所表示的實際數字,原碼:1000 0000
通過上面兩個例子,我們看到,不管是用原碼還是反碼,都不能正確的表達結果。另外,我們可以發現,在原碼和反碼中,對於0的表示有兩種,一種是0000 0000
(+0),一種是1000 0000
(-0),這也是一種缺陷,最好的方式是在給定乙個十進位制整數的範圍內,該範圍內的每乙個數在相應的二進位制表示中有且只有乙個與之對應,也就是存在一一對映的關係。那麼如何解決上述的問題呢?
終於等到補碼登場了,一般主角都壓軸,哈哈。補碼,就是在反碼的基礎上再加1,比如-1的補碼就是1111 1110
+1
=1111 1111
,用補碼來進行上面的計算就是:
反碼 十進位制
0000 0001 1
+ 1111 1111 -1
----------------
1 0000 0000 0 // 最高位的1被丟棄
結果為0,符合我們的實際邏輯。我們再來看看前面提到的一一對映的問題,在c/c++中,char
表示乙個8bit的有符號整數,在計算機中用補碼表示,所能表示的十進位制範圍為[-128, 127]
共256個數,那麼這個範圍是怎麼得來的呢?
補碼 十進位制
0000 0000 0
0000 0001 1
......
...0111 1111 127(2^7 - 1)
1000 0000 -128(-2^7 + 0)
1000 0001 -127(-2^7 + 1)
......
...1111 1111 -1(-2^7 + 127)
從上面可以看出,前128個數表示正數(最高位以0開始),後128個表示負數(最高位以1開始)。對於unsigned char
來說,由於其為無符號數,所以所能表示的最大範圍為[0, 255]
。對於其它的整數資料型別可以通過類似的方法得到他們的表示範圍,感興趣的童鞋可以自己算算試試。
對於float
來說,在一般機器上都是以四個位元組即32bit來表示,那麼它在計算機中是怎麼表示的呢?
ieee 754規定,對於32位的浮點數,最高位的那位表示符號位s
,緊接著的8位是指數e
,剩下的23位為有效數字m
或者稱為尾數。
圖1 32位浮點數在計算機中的表示
圖2 個人理解
在十進位制中,對於任何乙個小數都可以用科學計數法來表示,比如123.567
的科學計數法就是1.23456*10^2
,0.0097
就是9.7*10^-3
;同樣地,在二進位制的世界中,任何乙個小數也可以用類似的科學技術法來表示,只不過不在以10
為底,而是以2
為底。那麼對於十進位制的123.567
和0.0097
在二進位制中如何表示呢?
十進位制小數:123.567
在二進位制中的表示:
1. 首先取整數部分123,用二進位制中表示為1111011。
2. 小數部分.567如何表示?
我們都知道,在十進位制整數進行二進位制轉化的時候有乙個方法,就是對十進位制數進行不斷的除以2,然後取餘數(要麼為0,要麼為1),然後按逆序排列就得到了十進位制整數的二進位制表示。
那麼對於十進位制的小數該如何用二進位制表示是否找到了靈感?對,就是對小數部分不斷的乘以2,然後對得到的新小數取整數部分(要麼為0,要麼為1)直到小數部分為0或者已經達到精度上限(比如所取得0、1已經達到23位了,再繼續乘下去,對於32位的float來說,已經不能繼續儲存了)。
0.567 * 2 = 1.134 …… 取1 , 基數 = 0.134
0.134 * 2 = 0.268 …… 取0 , 基數 = 0.268
0.268 * 2 = 0.563 …… 取0 , 基數 = 0.563
0.563 * 2 = 1.126 …… 取1 , 基數 = 0.126
…………
……最終結果為:10010001001001101110100。(23位)
所以.567在二進位制中的表示為:.10010001001001101110100。
因而123.567在二進位制中的表示為:1111011.10010001001001101110100。
用科學計數法表示為:1.1110 1110 0100 0100 1001 1011 1010 0 * 2^6。
通過ieee 754的規定,我們可以很容易將其在計算機中表示:
符號位:0
指數字:exp - 127 = 6 --> exp = 133 -->1000 0101
尾數部分:1110 1110 0100 0100 1001 101
【ieee 754規定,在計算機內部儲存m時(1.******x),預設這個數的第一位總是1,因此可以被捨去,只儲存後面的******部分。】
所以123.567在計算機中的完整表示就是:
0 1000 0101 1110 1110 0100 0100 1001 101
圖3 123.567在計算機中的表示
這裡稍微再提一下,在c/c++中,float
所能保持的精度一般為小數字6-7位,那麼這個6或7是怎麼得來的呢?通過上面我們知道,對於32位的浮點數,後23位表示的是小數部分,總共可以表示2^23 = 8388608
,也就是說,後面的23位最多可以表示十進位制小數的前7位小數字,通過四捨五入,至少可以保證6位的精度是正確的,至於第7位......呵呵!
好了,通過上面的描述,基本了解了float
在計算機記憶體是如何表示的了,那麼double
在計算機中如何表示呢?其實和float
的表示原理是一樣的,只是double
一般用64位bit來表示,最高位還是符號位,但是指數部分用了11位來表示,而剩餘的52位全用來表示尾數(所能表示的精度一般為2^52 = ?
15-16位),其餘的規則和32位的float
沒啥區別,所以這裡就僅放一張圖,各位童鞋可以慢慢去體會。
圖4 64位浮點數在計算機中的表示
計算機中的浮點數
寫這篇blog,是因為在上工程碩士數學時候,又開始講到了浮點數的儲存,運算和精度的問題。這個問題已經見了好多次了,從微機原理的課到計算機視覺處理矩陣時候的conditioning,到這次。但感覺一直都沒有理清楚。所以這次嘗試梳理一下。一般主要分為兩種形式,這裡以8位的儲存進行解釋 所以用浮點數表示數...
浮點數在計算機中的表示
浮點數在計算機中的表示 最後編輯於 2010 4 13 計算機中數字是以0和1二進位制儲存的,我們熟悉的是整數的如何在計算機中表示,那麼浮點數是如何表示的呢?一 轉換 我們先來看看如何將十進位制的浮點數轉換成二進位制。乙個十進位制的浮點數,例如 abcd.efg 其中a g為0.9 其值用多項式為 ...
浮點數在計算機中的儲存
float和double在儲存方式上遵從ieee規範!例如 8.25 整數部分 除2取餘倒排 除法商餘數 8 24 04 220 2 21 01 201 餘數倒排 1000 小數部分 乘2取整順排 乘法 積整數部分 0.25 2 0.50 0.5 2 1.01 整數部分順排 01 前兩步結合的結果為...