2 的補碼(2’s complement),以下簡稱補碼。幾乎所有的計算機都通過補碼來表示有符號整數,在 C 語言中,數據類型 int
代表長度爲 4-Bytes (32-bits) 的有符號數,可表示的範圍爲 ,其中正數部分的補碼與我們的直覺相同,負數的補碼形式需要一些轉換,本文介紹補碼的各種轉換以及其內在的原理。
補碼的轉換算法可總結爲,按位取反再加一,例如要想知道 -30 在計算機中表示的形式(以 8-bits 爲例),可以這樣操作:
先寫出 30 的二進制形式
0 0 0 1 1 1 1 0
按位取反
1 1 1 0 0 0 0 1
再加一
1 1 1 0 0 0 1 0
這就是 -30 的補碼
讀懂補碼
如何知道補碼錶示的數是多少,若最高位爲 0,則代表是正數,可直接按照正常步驟轉化爲十進制,若最高位爲 1,則代表是負數,要知道負數的補碼形式代表的數字,可以先想辦法翻轉補碼的符號,這樣就得到的其正數表示,之後便輕而易舉了。而翻轉符號的方法也是,按位取反再加一。
1 1 1 0 0 0 1 0
表示什麼呢?
按位取反
0 0 0 1 1 1 0 1
再加一
0 0 0 1 1 1 1 0
這是二進制形式的 30,於是 1 1 1 0 0 0 1 0
代表的是 -30。
int
的範圍
在介紹部分說到在 C 語言中,數據類型 int
代表長度爲 4-Bytes (32-bits) 的有符號數,可表示的範圍爲 ,這是如何得到的呢?
最小整數
觀察下面這個 32-bits 的數
1000 0000 0000 0000 0000 0000 0000 0000
最高位爲 1,代表是負數,對他進行符號翻轉,按位取反再加一,得到
1000 0000 0000 0000 0000 0000 0000 0000
這是二進制形式的 ,可知 是 int
能表示的最小整數
最大整數
觀察下面這個 32-bits 的數
0111 1111 1111 1111 1111 1111 1111 1111
最高位爲 0,代表是正數
這是二進制形式的 ,可知 是 int
能表示的最大整數
爲什麼選擇補碼
補碼有簡化計算機內部算術單元電路的優勢,若採用補碼,加減法的電路可以統一,請看下面的例子:
計算 16 + 30
0 0 0 1 0 0 0 0 (16)
- 0 0 0 1 1 1 1 0 (30)
——————————————————————
0 0 1 0 1 1 1 0 (46)
計算 16 - 30
也即 16 + (-30)
0 0 0 1 0 0 0 0 (16)
- 1 1 1 0 0 0 1 0 (-30)
———————————————————————
1 1 1 1 0 0 1 0 (-14)
對於結果 1 1 1 1 0 0 1 0
,對其進行符號翻轉 (按位取反再加一) 後得到
0 0 0 0 1 1 1 0
這是二進制形式的 14,與預期相符。
從上面兩個例子可以看出,加減法的算法是統一的。
爲什麼是 “按位取反再加一”
爲什麼 “按位取反再加一” 能夠奏效呢?看看下面這個故事:
要想把正數的符號翻轉,可以通過 0 減去這個正數,就得到了他對應的負數。例如要得到 30 對應的負數:
0 0 0 0 0 0 0 0 (0)
- 0 0 0 1 1 1 1 0 (30)
——————————————————————
1 1 1 0 0 0 1 0 (-30)
雖然還可以繼續減下去,但由於我們限制在 8 位,因此計算機不會繼續計算了。
發現了嗎,得到的結果是 -30 的補碼。但其實這樣算和用 (此處 n = 8,代表一個 1 後跟了 8 個 0) 減去 30 是一樣的,因爲我們一直在像高位“借一”:
1 0 0 0 0 0 0 0 0 (2^8)
- 0 0 0 1 1 1 1 0 (30)
————————————————————————
0 1 1 1 0 0 0 1 0 (-30)
而 1 0 0 0 0 0 0 0 0
正好等於 8 個 1 再加 1:1 1 1 1 1 1 1 + 1
,而用 1 1 1 1 1 1 1 1
減去一個數相當於對這個數按位取反。
這就是按位取反再加一背後的故事。
參考
Two’s Complement, Cornell
關於 2 的補碼, 阮一峯