計算機中帶符號的整數為何採用二進位制的補碼進行儲存?

weixin_34119545發表於2019-01-08

                 計算機中帶符號的整數為何採用二進位制的補碼進行儲存?

  我們都知道在計算機內部資料的儲存和運算都採用二進位制,是因為計算機是由很多電晶體組成的,而電晶體只有2種狀態,恰好可以用二進位制的0和1表示,並且採用二進位制可以使得計算機內部的運算規則簡單,穩定性高。在計算機中存在實數和整數,而整數又分為無符號整數和有符號整數,無符號的整數表示很簡單,直接採用其二進位制形式表示即可,而對於有符號數的表示卻成了問題,如何表示正負?如何去處理正負號?下面來具體說下其中的原因,在這之前先了解一下原碼、反碼和補碼這幾個概念。

1.原碼、反碼和補碼的概念

  在瞭解原碼、反碼和補碼之前先說一下有符號數和無符號數。用過C語言的都知道在C語言中用signed和unsigned來標識一個數是否是有符號還是無符號型別的。對於一個8bit的二進位制來說,若當做無符號數處理,其能表示的整型值範圍是0~255,但是這樣表示資料就有個侷限性,如果資料是負的該如何表示?因此就引入了有符號型別的概念,對於有符號型別,規定取最高位為符號位,若最高位為0,則為正數,否則為負數,這樣一來對於8位二進位制,示數值的就只有7位了,能夠表示的非負數值範圍變為0~127,負值範圍為-127~-1,相當於可以理解為將無符號型別能夠表示的128~255拿來去表示-127~-1了。事實上,在計算機內部儲存中,計算機自己是無法去區分無符號還是有符號型別的,對於255和-1,在計算機內部儲存的都是11111111。換個角度來說,如果事先知道記憶體中儲存了這樣一個8位二進位制11111111,但是誰也不能肯定它具體表示什麼數值,是-1還是255?這個是需要靠程式設計師自己去指定的,如果指定為無符號型別,則編譯器則通過相應指令將其轉換為數值255。事實上對於-x的二進位制補碼錶示形式和(256-x)(256-x當做無符號型別處理)的二進位制表示形式相同,從這裡可以略微瞭解了補碼的含義了。在教材中對於原碼、反碼以及補碼一般是這麼定義的:

  對於正數原碼、反碼以及補碼是其本身。負數的原碼是其本身,反碼是對原碼除符號位之外的各位取反,補碼則是反碼加1。

  因為(-x)的二進位制補碼形式和256-x的二進位制表示形式相同,而255-x相當於對x的每一位取反,那麼256-x就是255-x後加1。

  注意:1)原碼、反碼、補碼的概念是針對有符號型別而言的。

     2)實數始終是有符號型別的(實數並不是採用補碼形式儲存的,具體可參考《淺談C/C++的浮點數在記憶體中的儲存方式》一文),整型資料包括無符號和有符號型別的。

2.採用補碼錶示帶符號的整數的原因

  對於有符號型別的整數,有原碼、反碼和補碼三種形式,最後選擇了補碼來表示,具體來說有下面幾點原因。

  1)能夠統一+0和-0的表示

  採用原碼錶示,+0的二進位制表示形式為0 000 0000,而-0的二進位制表示形式為1 000 0000;

  採用反碼錶示,+0的二進位制表示形式為0 000 0000,而-0的二進位制表示形式為1 111 1111;

  採用補碼錶示,+0的二進位制表示形式為0 000 0000,而-0的二進位制表示形式為1 111 1111+1=1 0000 0000,因為計算機會進行截斷,只取低8位,所以-0的補碼錶示形式為0000 0000。

  從上面可以看出只有用補碼錶示,+0和-0的表示形式才一致。正因為如此,所以補碼的表示範圍比原碼和反碼錶示的範圍都要大,用補碼能夠表示的範圍為-128~127,0~127分別用00000000~01111111來表示,而-127~-1則用10000001~11111111來表示,多出的10000000則用來表示-128。因此對於任何一個n位的二進位制,假若表示帶符號的整數,其表示範圍為-2^(n-1)~2^(n-1)-1,且有MAX+1=MIN。看下面一段程式碼:

char ch=127;
ch++;

  ch的值是多少?它的值是-128,讀者可以上機驗證一下。

  假如不採用補碼來表示,那麼計算機中需要對+0和-0區別對待,顯然這個對於設計來說要增加難度,而且不符合運算規則。

  2)對於有符號整數的運算能夠把符號位同數值位為一起處理

  由於將最高位作為符號位處理,不具有實際的數值意義,那麼如何在進行運算時處理這個符號位?如果單獨把符號位進行處理,顯然又會增加電子器件的設計難度和CPU指令設計的難度,但是採用補碼能夠很好地解決這個問題。下面舉例說明:

  比如-2+3=1

  如果採用原碼錶示(把符號位同數值位一起處理):

  1 000 0010+0 000 0011=1 000 0101=(-5),顯然這個結果是錯誤的。

  如果採用反碼錶示

  1 111 1101+0 000 0011=1 0000 0000=0 0000000=(+0),顯然這個結果也是錯誤的。

  如果採用補碼錶示

  1 111 1110+0 000 0011=1 0000 0001=0000 0001=(1),結果是正確的。

  從上面可以看出,當把符號位同數值位一起進行處理時,只有補碼的運算才是正確的。如果不把符號位和數值位一起處理,會給CPU指令的設計帶來很大的困難,如果把符號位單獨考慮的話,CPU指令還要特意對最高位進行判斷,這個對於計算機的最底層實現來說是很困難的。

  3)能夠簡化運算規則

  對於-2+3=1這個例子來說,可以看作是3-2=1,也即[3]+[-2]=1,從上面的運算過程可知採用補碼運算相當於是

  [3]+[-2]補=[1]補,也即可以把減法運算轉換為加法運算。這樣一來的好處是在設計電子器件時,只需要設計加法器即可,不需要單獨再設計減法器。

  總的來說,採用補碼主要有以上幾點好處,從而使得計算機從硬體設計上更加簡單以及簡化CPU指令的設計。

測試程式碼

#include<stdio.h>

int main(void)
{
    char ch=-1;
    char *p=(char *)&ch;
    unsigned char uch=*p;
    printf("%d\n",uch);   //輸出結果為255
    return 0;
}

相關文章