痞子衡嵌入式:不可不知的計算機原理知識(1)- 整數表示(原碼/反碼/補碼)

痞子衡發表於2017-03-26

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家講的是計算機原理知識點-整數表示

  本系列痞子衡會給大家介紹一些關於計算機原理的有趣的知識點,今天是系列第一節課,痞子衡要講的是計算機中最基礎的知識,即整數資料表示法。
  現實生活中的所有資訊在計算機看來就是一堆資料,計算機的工作就是和資料打交道。簡單來說,計算機最核心的功能就是兩個:儲存資料、處理資料。計算機首先得要能正確地儲存使用者資料,有了資料之後,計算機還要能夠按照使用者要求處理(運算)資料。今天痞子衡要講的就是資料(整數)在計算機中是怎麼儲存(表示)的。

一、何謂進位制

  學過數位電路的朋友肯定知道資料的進製表示法,常見的有二進位制、八進位制、十進位制、十六進位制表示法。在現實生活中我們所用的資料表示法是十進位制,即由0-9共10個數字表示所有數值。
  任意進位制轉換成十進位制的公式為Xn-1X...X1X0=Xn-1*Rn-1+...X1*R+X0,其中R為當前被轉換進位制的權重值,二進位制則R=2,八進位制R=8,十進位制R=10,十六進位制R=16。X可取值當前被轉換進位制所有的單個數字(值)。
  計算機就是大規模整合數位電路,數位電路只有兩種電平狀態(低/高),對應到數字表示就是0,1,所以實際上計算機只認二進位制,所有儲存在計算機中的資料都是由二進位制來表示的。

二、整數編碼

2.1 正負數表示(原碼)

  資料在計算機中都是用二進位制來表示的。對於無符號整數(正整數),比如uint8_t型,8個bit均為資料位,用二進位制表示其範圍為8'b00000000 ~ 8'b11111111(0~255),這種所有bit均為資料位以表示無符號整數的編碼方式即為原碼
  但很多時候我們不僅要儲存正整數,還要儲存負整數,此時便涉及到有符號整數的表示方法了,此時編碼規定最高bit表示符號(0為正,1為負),其餘bit表示資料位,這種表示有符號整數的編碼方式實際上也是原碼。對於int8_t型,bit7為符號位,bit6-0為資料位,用二進位制表示其範圍為8'b10000000 ~ 8'b01111111(-128~127),有朋友可能會對這裡有疑問,似乎按照編碼規定能表示的整數範圍應該是-127 ~ 127,但實際並不是如此,具體痞子衡在下面會解釋。

2.2 減法的實現(反碼)

  數位電路里我們都聽說過加法器,但沒有減法器的說法,說明計算機實際上僅支援加法運算,對於正整數做加法運算用原碼沒有任何疑義。那麼對於無符號整數做減法運算(等同於有符號數的加法運算)怎麼實現?我們嘗試著用原碼來代入運算,比如129-7(即129+(-7)):

   8'b10000001(129原碼)
 + 8'b10000111(-7原碼)
--------------
 = 8'b????????(符號位如何參與運算?)
 =9'b100001000(不區別符號位,直接運算,最高bit由於溢位被自動丟棄)
 = 8'b00001000(結果為8)

  我們似乎遇到了困難,有符號運算數的符號位如何參與運算?如果我們不區別對待符號位,直接把它當做資料位進行計算,得出的結果顯然是不對的,所以原碼是無法被用作減法/有符號數的計算的。為了解決這個問題,此時引入了第二種編碼方式規定(反碼),即正數的反碼與原碼相同,負數的反碼等於原碼除了符號位外,其餘資料位全部取反。有了反碼,此時我們用反碼重新計算129-7:

   8'b10000001(129反碼)
 + 8'b11111000(-7反碼)
--------------
 =9'b101111001(最高bit由於溢位被自動丟棄)
 = 8'b01111001(結果為反碼,由於最高bit為0,即表示正數,所以等同於原碼,其值為122)

2.3 消除+/-0之爭(補碼)

  反碼看起來似乎解決了減法問題,不妨讓我們再做一個減法運算,比如5-5,我們嘗試再一次用反碼解決問題:

   8'b00000101(5反碼)
 + 8'b11111010(-5反碼)
--------------
 = 8'b11111111(結果為反碼)
 = 8'b10000000(轉換成原碼,根據有符號數原碼編碼規定解析為-0)

  -0是等於0的,運算的結果是對的,但是不是總感覺哪裡不對?是的,原碼8'b00000000表示+0,我們已經有+0來表示0了,再用-0表示0顯得多此一舉,浪費了一個編碼值。怎麼解決這個問題?此時引入第三種編碼方式規定(補碼),正數的補碼與原碼相同,負數的補碼等於反碼加1.那現在我們嘗試用補碼來重新計算5-5:

   8'b00000101(5補碼)
 + 8'b11111011(-5補碼)
--------------
 =9'b100000000(最高bit由於溢位被自動丟棄)
 = 8'b00000000(結果為補碼,由於最高bit為0,即表示正數,所以等同於反碼,也等同於原碼,其值為+0)

  上述運算中使用補碼消除了運算結果為-0的問題,但還是沒有告訴我們8'b10000000的值到底是多少?顯然不應該是-0,那麼應該要怎麼解析8'b10000000?有朋友說答案是-128,是的,前面我們已經在原碼一節中給出了答案,那如何來理解這個答案?首先我們得要從標準原碼定義的角度來說-128,按照定義-128的原碼應該是9'b110000000,轉換成補碼還是9'b110000000,原碼與補碼一致,截斷最高位後可得8'b10000000,講到這裡,我們似乎有點明白-128的由來了,但這樣真的靠譜嗎?代入運算會不會影響運算結果的正確性?我們來試試看-128+17:

   8'b10000000(-128補碼)
 + 8'b00010001(17補碼)
--------------
 = 8'b10010001(結果為補碼)
 = 8'b10010000(轉換為反碼)
 = 8'b11101111(轉換為原碼,由於最高bit為1,即表示負數,其值為-111)

  由於用8'b10000000來表示-128參與運算並不會導致結果錯誤,那麼何不直接約定用8'b10000000來表示-128呢?好,就這麼愉快的決定了!這就是-128=8'b10000000的由來。其實就是計算機裡的一個異常的人為規定而已!

2.4 編碼小結

  無論uint8_t/int8_t型資料全部使用補碼的方式在計算機中進行儲存,其中int8_t型資料範圍內-128是個異常規定,無法用補碼編碼規則直接解釋,但計算機可以自動理解識別。
  為了鞏固知識,我們做個小練習:

// a,b,c,d,e,f分別等於多少?
uint8_t a = -1;
uint8_t b = -128;
uint8_t c = -129;
int8_t d = 128;
int8_t e = 129;
int8_t f = -129;
// 答案: a = 255, b = 128, c = 127, d = -128, e = -127, f = 127

  至此,計算機原理知識點之整數表示痞子衡便介紹完畢了,掌聲在哪裡~~~

相關文章