天天看點

深入了解整數在計算機内部的表示1. 無符号整數的表示2. 補碼表示

1. 無符号整數的表示

    我們知道,無符号整數在計算機内部是以二進制的形式存儲的,比如我們在C語言中聲明并初始化一個變量:

int i = 66;      

    假設我們針對的機器是32位機器,位元組順序(byte order)為小端(little endian)。由于int類型是32位的,66這個數字就會被存儲為它的32位二進制表示(01000010 00000000 00000000 00000000),共占據4個存儲單元。

    這樣一來,32位二進制數所能表示的無符号整數共有2^32個,範圍為[0, 2^32 - 1]。然而,我們需要計算機計算的内容常常會包含負整數,是以先得使得計算機能夠“認識”負整數。這就要求我們制定一種規則,對于給定的32位二進制數,這種規則要能夠準确的說明它表示的是負整數還是正整數,并進一步說明值是多少。在這種需求下,人們首先發明了原碼這種能同時表示正整數和負整數的編碼規則。原碼的規則很簡單,拿32位二進制數來說,把它的最高有效位(most significant bit,msb)規定為符号位,1的話這個數就是個負數,0的話這個數就是個正數,其餘的31位來表示這個數的值是多少。也就是說原碼把32位拆成了1位的符号位和31位的數值位序列。

    然而原碼存在兩個問題,一是數值0的表示有兩種:一種是msb為1其餘位為0(-0),一種是msb為0,其餘位為1(0);還有一個更頭疼的問題是互為相反數的兩個數(除了0)相加不等于0...于是人們又發明了反碼。反碼也是msb作為符号位,然後其餘的31位,将一個數的低31位按位取反,即可得到它對應的相反數。在反碼這個編碼體系下,兩個互為相反數的數相加倒是等于0了,不過數值0依舊有兩種形式,一個32位全為0的+0,一個是32位全為1的-0。

    我們不僅僅想讓數值0隻有一種二進制表示,而且我們還想讓有符号數和無符号數能使用同一套加法器及乘法器。基于這些需求,補碼便誕生了。

2. 補碼表示

    在補碼表示中,是将32位二進制數能表示整數的範圍劈成兩半,一半給負整數,一半給非負整數。那麼很自然的劃分成一半一半的方法就是根據msb為0或1來劃分。補碼表示下,msb為1表示這個數是負整數,為0表示是非負整數。 

(1)有符号整數與無符号整數

    我們先來理清一下無符号整數和有符号整數的概念。首先,我們要知道的是,計算機壓根就不認識有符号和無符号,對于計算機來說,有符号整數和無符号整數都是一串位序列罷了。一串32位的位序列究竟代表是有符号數還是無符号數,完全是由上下文決定的。比如記憶體中有這樣一串位序列:11000000 00000000 00000000 00000000。假如我們現在知道它表示一個整數,我們是否能知道它究竟是正數還是負數嗎?答案是不能。除非我們知道這個位序列是一個int型變量的值,那麼我們可以知道它是有符号整數,是以這串序清單示-64;而如果這是一個unsigned型變量,它的值就是193。是以,所謂有符号整數和無符号整數,就是一種上下文。在有符号整數這個上下文的前提下,我們說 11000000 00000000 00000000 00000000表示-64,因為它的符号位為1;而在無符号整數這個上下文下,所有的數都被看做是正的,根本沒有符号位這一說,是以相同的位序列就表示192。

(2)為什麼選擇補碼表示

    說到補碼表示的優越性,我們先要來了解一個概念:同餘運算。在這之前,我們先介紹下模運算(mod)的概念,模運算和取餘運算(一般用“%”表示)的差別在于取餘運算計算商時向0取整,模運算計算商時向負無窮取整。例如,-3 % 5的值-3,計算過程是将商-0.6向0取整得到0,然後-3 - (0 * (-3))得到結果為-3。而-3 mod 5的值為2,計算過程是将商-0.6想下取整得到-1,然後-3 - ( -1 * 5 )得到2。現代計算機的加法與乘法運算單元實際上進行的都是“同餘運算”。在32位體系下,我們定義2^32為這個同餘體系下的模。我們用同餘式表示一個同餘運算,“≡”為同餘符号,相當與等式中的等号。加法器計算1 + 1實際上進行的就是同餘加法,大緻分如下三步:

  1. a ≡ 1 (mod 2^32)
  2. b ≡ 1 (mod 2^32)
  3. c ≡ a + b ≡ 2 (mod 2^32)

   那麼我們來看一下,在32位體系下,(x mod 2^32)的結果範圍為[0, 2^32-1]。特别的,我們注意到:2^32 - 1 ≡ -1 (mod 2^32) ≡ 2^32 - 1 (mod 2^32),也就是說-1與2^32 - 1是同餘的,同理,-2 與 2^32 - 2,-3 與 2^32 - 3都是同餘的。那麼我們就可以定義11111111 11111111 11111111 11111111表示-1和2^32 - 1,前者是在有符号整數的上下文中,後者是在無符号整數的上下文中。

    我們再來看一下這個同餘體系下,-x與x的關系。假設x是一個32位序列,很容易知道x + ~x會得到11111111 11111111 11111111 11111111,。用同餘式表達就是:x + ~x ≡ 2 ^32 - 1  ( mod 2^32)。而x + ~x + 1 ≡ 2^32 (mod 2^32) = 0 (mod 2^32)。是以在這個同餘體系下我們可以得到-x ≡ ~x + 1 (mod 2^32)。這也就是我們常說的一個負整數的補碼表示為它的相反數的補碼表示按位取反再加一。