1. 程式人生 > >詳解原碼、反碼、補碼——深入理解補碼

詳解原碼、反碼、補碼——深入理解補碼

  學過計算機原理的人都知道原碼、反碼、補碼,但是有多少人知道為什麼會有這三種碼呢,這三種碼又是用來幹嘛的呢。
  眾所周知,在計算機的世界只有01,那麼顯然所有的數都得轉成二進位制,這樣計算機才能夠理解。如何將一個十進位制的數轉成二進位制就不說了,說下原碼,正數的原碼就是十進位制轉成二進位制得到的二進位制值,而負數是對應的正數轉成二進位制得到的二進位制值,然後將最高位(符號位)置為1表示這是一個負數,如-10:10001010。

1. 原碼

  計算機進行算術運算時為了簡單效率所以要求能夠使用加法代替減法,如1-1==1+(-1)==0,那麼我們先看看原碼能不能實現這種需求。
示例:

計算76-10==66
   十進位制     二進位制
76 01001100 + -10 10001010 --------------------- 66 11010110(-86)
2. 反碼

  從上面算出的結果可見原碼是無法完成對減法的運算需求的,那麼由於1-1==1+(-1),所以人類又找到了一個看似能夠解決這個問題的解決方法——反碼,即將負數的符號位不變其餘位取反。下面我們再看看反碼能不能解決問題。
示例1:計算15-125

計算15-125==-110
   十進位制     二進位制原碼    二進位制反碼
   15        00001111    00001111
 +  -125      11111101    10000010
---------------------------------
-110 11101110 10010001 得到10010001(反碼)==11101110(原碼)==-110,正確。注意:使用反碼計算得到的結果也是反碼,需要再次轉換成原碼。

示例2:計算76-10

計算76-10==66
   十進位制     二進位制原碼    二進位制反碼
   76        01001100    01001100
 + -10       10001010    11110101
---------------------------------
   66        01000010    101000001==01000010  
這裡得到的值超過8bit,所以最高的1需要丟棄,丟棄後需要在最低位+1,得到01000010(反碼)==01000010(原碼)==66,正確。

示例3:計算1-1

計算1-1==0
   十進位制     二進位制原碼    二進位制反碼
   1         00000001    00000001
 + -1        10000001    11111110
---------------------------------
   0         10000000    11111111  
得到11111111(反碼)==10000000(原碼)==-0,-0?通過反碼計算會出現+0和-0,一個0對應了兩個碼,顯然是不合理的。

  從上面三個例子可以看出使用反碼進行減法運算時存在兩個問題:
  1. 當計算結果溢位時需要額外進行+1操作,使得運算多了一步,效率降低
  2. 0存在+0和-0兩種存在方式,不方便理解

3. 模與互補、同餘

  在看補碼之前,先介紹三個概念——模、補數、同餘。我們從現實生活舉例來看:

  • 我們將一個時鐘的分針往前撥20分鐘,和往後撥40分鐘,得到的結果是一樣的。
  • 把你的屬年(屬猴)往後退5年,和往前進7年,一樣都是屬兔。
  • 把數字 87,減去 25,和加上 75,在不考慮百位數的條件下,得到的結果都是62。

  上述幾組數字,有這樣的關係:
    20 + 40 = 60
    5 + 7 = 12
    25 + 75 = 100
  式中的 60、12 和 100,就是“模”。
  式中的 20和40、5和7,以及25和75,就是一對對“互補”的數字。
  而且20,80,140在模是60的情況下就是互為“同餘”的數字。
  通俗解釋下模、補數、同餘的概念:

  • :就是一個輪迴,比如分針轉一圈,十二生肖一輪等等。
  • 互補 :一個數值針對某個模的互補值就是這個數值加上或者減去多少能夠等於模,或者等於模的同餘值。
  • 同餘 :一個數值加上或者減去模的整數倍得到的所有數值即為該數值的同餘值(也就是除上模,餘數是一樣,所以叫同餘),0是模的同餘,-模也是模的同餘。

  理解了什麼是模,什麼是互補、什麼是同餘,那麼如果給一個模,以及一個值a,如果計算a的補數(與a互補的值)呢,其實很簡單,只需要拿模-a即可,計算同餘值可以直接加上或者減去模的整數倍即可。

4. 那麼互補的值有什麼用呢?

  如果我們在進行減法運算時,用與減數互補的值代替減數與被減數進行加法運算會發生什麼呢?廢話不多說,看示例。
示例1:在分鐘刻度下,計算55分鐘往後撥動34分鐘,轉化成數學計算就是:55-34

被減數      55
減數        34
減數補數   60-34==26
最終結果     55+26==81
---------------------
用減數補數代替減數得到結果為81,81在分鐘刻度盤上正好是21,也就是81是21的同餘值,和55-34是一樣的。注意:這裡涉及到類似上面的87+75的情況,即忽略了進位。

示例2:在十二生肖中,計算猴年往後退11年,轉化成數學計算就是:9-11

被減數      9
減數        11
減數補數   12-11==1
最終結果     9+1==10
-------------------
用減數互補值代替減數得到結果為10,10對應到十二生肖正好是雞,和猴年往後退11年是一樣的,所以得到的也是一個同餘值。

  從上面的示例可以看出,使用互補值計算出的結果與實際值其實是同餘的關係。

5. 二進位制的模

  上面看了分鐘刻度盤的模,十二生肖的模,以及兩位整數的模,那麼對於一個8bit的位元組的模是多少呢?
  分鐘刻度盤的模為什麼是60?是因為他的值是從1-59,總共60個值,十二生肖以及兩位整數也是一樣的,所以我們只需要看看一個8bit的位元組的所有取值一共是多少個就是他的模,顯示8個bit可表示的最小值是00000000==0,最大值是11111111==255,那麼從0到255一共是256個值,所以一個8bit的位元組的模就是256了。但是其實在計算機中為了能夠表示負數,所以講8bit的位元組的最高位設為符號位,0表示整數,1表示負數,所以能夠表示數值的也就只有7bit,如果我們忽視符號位,那麼剩下7bit的模就是128,而不是256了。下面在計算時我們會直接使用128而非256!

6. 使用互補值進行二進位制的減法計算

  下面我們就來看看如果使用互補值來進行二進位制的減法計算,我們先來看一個公式:假設模式M,我們計算X-Y,然後我們使用減數的補數來計算,看看下面的換算:

X-Y == X+(M-Y) == X+((M-1)-Y+1)

  下面我們來看示例,這個公式在下面會用到的。

示例1:計算15-125

           十進位制          二進位制
被減數      15             0001111
減數        125            1111101
減數補數   128-125==3     0000011
最終結果     18             0010010
------------------------------------
得到0010010==18,在模式128的情況下,18正好是-110的同餘值,跟上面現實的例子是一樣的!

示例2:計算76-10

           十進位制          二進位制
被減數      76             01001100
減數        10            00001010
減數補數   256-10==246   11110110
最終結果     322    101000010
------------------------------------
得到101000010==322,在模式256的情況下,322正好是66的同餘值,結果還是一樣!

  從上面兩個例子我們應該可以看出,如果我們使用減數的補數進行加法運算,那麼得到的結果就是一個與正確結果同餘的值。在現實生活中,我們可以直接把兩個同餘的值看做是相同的,例如分鐘20和分鐘80完全就是一樣的,那麼在計算機裡我們可以這麼假設嗎?答案是可以的,看下面。
  試想當計算機使用一個7bit的空間儲存一個數值時是如何儲存的,比如18,我們可以這麼推算,首先分配一個7bit的空間,每個bit上的值都是0,那麼如何表示18呢?我們可以這樣理解:往這個7bit的空間內進行18次加1操作,滿2就進1,最終就會得到0010010。那麼如何表示-110,我們可以理解為往這個7bit的空間內進行110次減1操作,一開始全是0,那麼如何減1呢?很簡單直接減成1111111即可,可以這樣理解,分鐘在0刻度,你往後撥一下就會指向59,這裡也是這個道理,所以連續減110次,就會得到0010010,根18是一樣的,所以在計算機看來18和-110是一樣的。
也就是說15-125 == 15+(128-125) == 15+(127-125+1) (上面的公式),也就是說-125被127-125+1代替了,那麼127-125+1(M-)又是什麼?

7. 補碼

  上面一路走來終於證明了使用補數可以代替減法,下面我們要解決的問題是M-1-Y+1是啥。
我們直接看二進位制如何計算M-1-Y+1。
示例:計算M=128,Y=110

     十進位制   二進位制
 M-1  127   1111111
 -Y   110   1101110
            0010001
--------------------
M-1換算成二進位制就是N位1,那麼N位1減去任何一個N位的二進位制是啥呢?其實就是按位取反!因為遇到0,1-0==1,取反,遇到1,1-1==0,取反,所以整體就是按位取反,也就是反碼。

 +1   1     0000001
            0010010
 ------------------
所以總體就是在110的二進位制基礎上按位取反然後加1,也就是110的反碼加1。

  看了上面的示例,應該知道M=128,Y=110,M-1-Y+1就是Y的反碼加1,也就是說,如果我們需要計算X-Y,只需要計算X+(Y的反碼+1),由於我們得出這個結論是使用補數替代減法得到的,所以Y的反碼+1就被叫做Y的補碼
  到這裡我們知道了110的補碼,上面我們介紹了計算機使用1位元組的最高位表示符號位,1表示負數,所以-110的最高位是1,由於在使用補碼進行減法運算過程中最高位並不參與運算,所以這個最高位應該是固定不動的,所以負數的反碼補碼最高位始終都是1。也就得到了-110的補碼是:10010010。對於正數,符號位是0,那麼反碼補碼最高位就始終是0,而且對於正數在計算時也無需使用其補碼進行操作,但是為了統一都是用補碼,所以定正數的反碼補碼都等於原碼。
  根據補碼的計算過程有些文章會說一個負數X的補碼對應的值==2^n-|X|,理解了上面的過程這個公式就自然懂了,不過這個公式沒啥用,也沒必要記。
  到這裡終於把補碼的來歷說清楚了,至少我自己是明白了,但願讀者也可以明白吧!

一些補碼的其他知識

  上面我們看了7bit的模式128,也就說是能表示0-127共128個數值,加上最高位的符號位就成了-127-127共計255個數值,因為沒有-0這個數字。但是實際對於計算機來說8bit的空間是可以表示256個數字的,那麼還有一個數字是啥呢?正是:10000000(注意:這是補碼,因為計算機都存的補碼)。我們可以試著計算下10000000的原碼,可以得到10000000的原碼就是10000000,也就是-0,但是如果存在+0和-0兩個計算機碼對應一個值(+0和-0都是0),那麼顯然是沒必要的,而且還會造成混亂,所以人為的規定10000000表示-128。所以一個8bit的空間可以表示的數字就是從-128到127了,而不是-127-127!

8. 參考文章

歡迎大家點評討論!