一文講清楚補碼的本質

在計算機中,所有的數字都是以二進制的形式表示的,即均爲 0 和 1 組成的各種編碼,數字的表示形式可以劃分成原碼,反碼和補碼

如何表示 原碼、反碼、補碼

如果沒有特殊說明,下面的介紹都是以 4 位二進制爲例的

爲了區分正數和負數,計算機中將二進制的最高位 (bit) 規定爲符號位,它等於 0 時表示正數,等於 1 時表示負數,剩下的所有低位( bit ) 用來表示數值

下面的圖片從左到右分別表示 +5 和 -5 的原碼

正數的反碼和其原碼相同,負數的反碼在原碼基礎上,符號位不變,數值位取反

下面的圖片從左到右分別表示 +5 和 -5 的反碼

正數的補碼和其原碼相同,負數的補碼在反碼基礎上加 1

下面的圖片從左到右分別表示 +5 和 -5 的補碼

爲什麼用補碼

在計算機中,數字是以補碼的形式進行存儲和參與運算的

這看起來比較奇怪,爲什麼要採用補碼這麼麻煩的方式表示數字 (特別是對於負數),直觀一點兒不好嗎?

爲了講明白這個問題,下面我們分別以原碼,反碼和補碼的形式來模擬二進制的加法和減法運算,以 4 位二進制爲例來說明

下圖列出了 4 位二進制所有正數和負數的二進制表示

對於 4 位二進制來說,最高位是符號位,也就是圖中黃色二進制的 0 和 1 的位置

用原碼模擬 3 + 2、6 + (-2) 、(-1) + (-3) 運算,具體的運算過程如下:

上圖中,圓圈中的二進制位是做加法運算的時候向前進位的結果,由於有效二進制位數是 4 位,所以圓圈中的二進制位因溢出而自動丟棄(在計算過程中仍然用到了溢出的二進制位,結果會丟棄溢出的二進制)

由計算過程可知,3 + 2 = 5 是正確的,但是 6 + (-2) = 0 以及 (-1) + (-3) = 4 結果都是錯誤的

所以,原碼雖然直觀易懂,也易於轉換,但是在運算上,正數之間的加法是沒問題,負數之間以及正數和負數之間都存在問題,因此計算機中不能用原碼錶示數字

下面列出了 4 位二進制所有正數和負數的二進制表示,請看下圖

正數的反碼是其自身,負數的反碼是符號位不變,其他位取反,圖中黃色二進制位表示符號位

用反碼模擬 3 + 2、6 + (-2) 、(-1) + (-3) 運算,具體的運算過程如下:

和原碼一樣,上圖中圓圈中的二進制位由於溢出而被自動丟棄,在反碼的運算中,只有 3 + 2 = 5 是正確的,其他的結果都不正確

注意:(-1) + (-3) 的結果是 1010,符號位爲 1,表示結果是負數,根據上面負數十進制對應二進制的反碼錶可知,它對應的十進制是 -5

由反碼運算結果來看,正數的加法結果是正確的,負數和負數以及正數和負數加法的結果是錯誤的,所以,計算機中也不能用反碼錶示數字

說完了原碼和反碼,現在來看下補碼,下圖是正數和負數補碼的二進制表示

用補碼模擬 3 + 2、6 + (-2) 、(-1) + (-3) 運算,具體的運算過程如下:

由上述計算過程可知,除去溢出的二進制位後,3 + 26 + (-2) 、(-1) + (-3) 計算的結果全都正確

用原碼和反碼的表示方式,都不能解決加法運算,但是用補碼錶示,不管是正數之間、負數之間還是正數和負數之間的加法,都可以解決

所以,計算機選擇用補碼來表示數字以及用補碼進行運算

補碼的好處

補碼在加法或減法處理中,不需因爲數字的正負而使用不同的計算方式。只要一種加法電路就可以處理各種有符號數和無符號數加法

而且減法可以用一個數加上另一個數的補碼來表示,因此只要有加法電路及補碼電路即可完成各種有符號數和無符號數加法及減法,在電路設計上相當方便

另外,根據上一小節中,+0 和 -0 的補碼都是 0000,對應的十進制是 0

也就是說 0 的補碼就只有一種表示方式,在計算機中也就有唯一的表示,這和反碼不同(在反碼中,0 有二種二進制表示方式),因此在判斷數字是否爲 0 時,只要比較一次即可

由於 +0 和 -0 的補碼只有一種表示方式,即 0000,但是原碼和反碼都有兩種表示方式,所以補碼會多出一種二進制表示方式 1000( 以 4 位二進制爲例 ),對應十進制數 -8

補碼是怎麼來的

前面講到 負數的補碼是其反碼加 1 , 爲什麼要這麼計算呢, 這麼計算就是有效嗎 ?

在十進制中,一個負數可以通過 0 減去一個正數得到,同樣的,二進制中也可以

比如:-3 可以表示成 0 - 3,也可以表示成下面的二進制減法計算

因爲 0 ( 0000 ) 小於 3 ( 0011 ),根據算術運算規則,當被減數的位小於減數時,需向前一位借 1

0000 向前一位借 1 後變成了 1 0000,於是,上面的減法計算就變成了

我們知道, 1 0000 可以表示成 1111 與 1 的和, 也即 1 0000 = 1111 + 1, 於是,計算就變成了

根據上面的計算,0 ( 0000) 減 3(0011) 的結果是 1101, 而 1101 剛好是 -3 的補碼

其實,上面的計算過程就相當於先求反碼然後加 1 , 請看下圖

用 1111 減去 3 的源碼 0011,結果是 -3 的反碼 1100,然後再加 1 ,得到 -3 的補碼 1101

再看看前面介紹的,負數的補碼等於其反碼加 1,是不是有點兒似曾相識呢,是的,負數的反碼就是這麼來的,它並不是一個毫無根據的定義,而是通過上面的計算一步一步得出來的,只不過補碼的計算方式剛好是其反碼加 1 而已

爲什麼補碼適合正數的加法

我們還是以 4 位二進制爲例來進行說明

假如有兩個正數 A 和 B,現在要證明 A 減 B 的結果等於 A 加上 B 的補碼

減去一個數等於加上一個負數,所以 A - B = A + (0 - B)

由上一小節可知, (0 - B) 等價於 (1111 - B) + 1

所以,A 加 B 的補碼就等於 A + (1111 - B) + 1,假如結果爲 R ,則有 R = A + (1111 - B) + 1

A + (1111 - B) + 1 可以寫成 A - B + (1111 + 1)

A - B + (1111 + 1) 又可以寫成 A - B + 1 0000 (1111 + 1 = 1 0000)

我們是以 4 位二進制爲例的, 1 0000 已經超過 4 位了,所以加 1 0000 時,最高位會因溢出而被丟棄

其實這時 1 0000 就相當於 0000 了(最高位溢出,需要丟棄)

所以,上面的計算

R = A + ( 1111 - B ) + 1
  = A - B + ( 1111 + 1 ) 
  = A - B + 1 0000
  = A - B +   0000
  = A - B

這樣就證明了 A 減 B 等於 A 加上 B 的補碼

小結

本文介紹了原碼、反碼以及補碼,重點闡述了補碼的由來以及證明了補碼計算正數加法的可行性

****推薦關注「算法愛好者」,修煉編程內功

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Ek_2B_Yg2X_YvldAtxUDqA