關於補碼的由來和作用

最近在讀《深入理解計算機系統》(CSAPP),第二章中關於補碼的描述很有意思,在書中並沒有詳細敘述補碼的由來和爲什麼要使用補碼來表示有符號數,而不是用原碼和反碼。相反這本書詳細的敘述了補碼的數學表示,以及公式的推導!對補碼的由來卻一筆帶過,甚至原碼和反碼只是簡單的在後面的籃框提示中提了一下,根本沒有出現在正文。

這在一定程度上造成了我的閱讀困難,於是在搜索引擎的幫助下,我查了很多資料,瞭解到補碼的更多細節,以及這個神奇的編碼方式如何幫助人們設計CPU時節省大量的精力和金錢。這裏我把他記錄下來。

計算機中的信息都是用二進制表示的,在表示無符號數時並沒有什麼問題,但是在表示有符號整數時就出現了問題,如何在二進制中區分正數與負數呢?
區分正數和負數的另一個意義在於如果能夠準確的表示出、負數,我們就可以化減法爲加法,如5-4就可以表示爲5+(-4),至於爲什麼那麼多先輩要執着於把減法化爲加法,原因很簡單,對計算機的電路而言,計算加法要比計算減法方便的多。
很多人提出了有符號整數的編碼方式,其中有:原碼(Sign-Magnitude)、反碼(ones' Complement)、補碼(two's Complement)。

原碼

原碼的英文爲sign-magnitude,第一個單詞爲符號的意思,第二個單詞經過查詢有“大小”的意思,大意應該爲符號-數值的組合,在原碼中,第一個數字代表符號位,1代表負數,0代表正數,餘下的爲數值位,用來表示具體的數值。這種編碼方式最簡潔易懂,也最符合人的思維。比如:-5用4位二進制原碼來表示就是1101。
CSAPP中給出的原碼數學公式爲:
TIM截圖20191105192713.png
由此我們能推斷出SMAXw=2w-1-1,SMINw=-(2w-1-1)。如八位二進制原碼的範圍應該爲[-127,127]。
使用原碼來讓機器做運算似乎是可行的,但是實際上,這種方法對機器來說卻是十分困難的,機器無法像人一樣判斷出0是+,1是-,設計出一個能讓機器判斷正負的電路也是十分複雜和困難的。
實際上目前我們很少用原碼來做運算,原碼目前常常用來作爲我們求補碼的一個過渡。

反碼

反碼的英文爲ones'complement,乍看之下有些迷惑,翻譯過來是“很多個1的補”,似乎更迷惑了。。。
其實反碼確實可以用“很多1的補”來理解,我們都知道w位二進制數表示的最大範圍是2w-1,用位向量來表示就是[111111....11111],就是很多個1。假設x是正數,求-x的補碼的含義實際上就是求[11111...111]-x,也就是(2w-1-x)(其中w爲位數),這是反碼的一種計算方式,另一種計算方式就是用原碼計算,原碼的符號位不變,剩下的數值位全部取反,比如1011的反碼就位1100。這和上面的計算方法得到的結果是一致的。
當然要注意的是上面所說的計算方法只對負整數有作用,正整數的反碼實際上和正常的無符號數編碼沒有區別。
反碼的另一種具體數學解釋是在CSAPP中看到的:
TIM截圖20191105192723.png
由此我們能推斷出OMAXw=2w-1-1,OMINw=-(2w-1-1)。如八位二進制原碼的範圍應該爲[-127,127]。
這個公式應該可以通過上面的提到的計算方法嚴格證明出來。使用此公式可以快速的算出反碼錶示的值。
反碼的優點是計算快捷,不僅僅是對人而言,計算一個負數的補碼,只需要將其的正數表示按位取反即可,對於機器來說,取反可比做加減法快多了。
反碼實際上可以用來做二進制運算,它的規則是從低位到高位逐列進行計算。1和1相加是0但要產生一個進位1,0和1相加是1,0和0相加是0。若最高位相加後產生進位,則最後得到的結果要加1。(反碼運算的原理將在下面的補碼運算中解釋)。
需要注意的是與原碼不同的是,反碼的運算符號位與數值一起參加運算。
用反碼運算,其運算結果亦爲反碼。在轉換爲真值時,若符號位爲0,數位不變;若符號位爲1,應將結果求反纔是其真值。

原碼和反碼的缺陷

原碼和反碼在不考慮性能的情況下確實能夠用來做二進制有符號整數的計算,歷史上也確有基於這兩種編碼方式的機器。但是這兩種編碼卻存在很多難以解決的缺陷。
考慮四位二進制數[0000],在原碼中表示0,在反碼中也表示0,而對於原碼來說[1000]也表示0,對於反碼來說[1111]也表示0。一般我們把[0000]稱爲+0,而另一種表示方法稱爲-0。
一個數字有兩種編碼方式,這顯然是不合理的!
爲了解決這種不合理我們引入了補碼的概念,它繼承了反碼易於計算的優點,也解決了原碼難以計算的問題。


介紹完了原碼反碼,在介紹補碼之前,爲了便於理解,我們首先要介紹一下模的概念:

“模”是指一個計量系統的計數範圍。如時鐘等。計算機也可以看成一個計量機器,它也有一個計量範圍,即都存在一個“模”。
例如:
時鐘的計量範圍是1~12,模=12。表示n位的計算機計量範圍是0~2^(n) -1,模=2^(n)。
“模”實質上是計量器產生“溢出”的量,它的值在計量器上表示不出來,計量器上只能表示出模的餘數。任何有模的計量器,均可化減法爲加法運算。
在計算機中,計算加法要比計算減法要容易和便宜,那麼到底如何在計量裝置中用加法代替減法呢?
這裏我們可以用時鐘舉一個例子:
TIM截圖20191104215527.png
這是一個只有時針的時鐘,目前它指向9。如果我們想讓他回退到4。我們有什麼辦法呢?
最簡單的方法是,倒退五個單位,9-5=4。但是這裏我們不想讓指針回退,是否有另外的方法能夠讓指針指向4呢?
答案是把指針向前撥7個單位。
TIM截圖20191104220151.png
爲什麼能夠這麼做呢,這就是前面我們提到的“模”的作用。
9+7=16=12+4,在時鐘這個計量機器上進位並不能顯示出來,所以最後時鐘上顯示的是4。
在以12模的系統中,加7和減5效果是一樣的,因此凡是減5運算,都可以用加7來代替。對“模”而言,8和4互爲補數。實際上以12模的系統中,11和1,10和2,9和3,8和4,6和6都有這個特性。共同的特點是兩者相加等於模。
同樣的概念在計算機中也完全成立,考慮一個四位的二進制數,它的範圍爲[0,255],模爲256,把這個概念擴展一下。w位的二進制數的模就爲2w
模就是補碼之所以能夠進行二進制計算的基本原理。


補碼

現在我們來談談本文的主角補碼(Two's complement),它的英文名依舊很迷惑,不過沒關係,下面我會用引入模的概念來幫助我們理解補碼的具體含義。
我們知道一個模爲10的計算系統中,-4與+6的結果是相同的(9+6=10+5=15,捨去進位最後的結果爲5與9-4相同),我們把這個概念擴展:
在一個模爲M的系統中,我們需要計算m-n,而-n在模爲M的系統中可以轉化爲+(M-n),所以只需要計算m+(M-n),這樣就成功的把減法轉換成了加法。
把其擴展到二進制中:
在一個位數爲w的計算系統中,我們需要計算m-n(n爲正整數),而-n在系統中可以轉化爲(2w-n),所以只需要計算m+(2w-n)並把溢出的位捨去,而-n的補碼計算方法就是爲2w-n。這也是英文名爲Two's complement的原因,意爲2的補,這裏只有一個2,所以用Two's complement。
補碼似乎已經十分完美了,它不僅解決了0的編碼重複問題(在補碼中0只有[0000]一種表示),也成功的把減法變成了加法,但是問題來了,爲了把減法變成加法而引入了減法不是像一個笑話嗎(手動狗頭)。
這個時候我們就需要用到原碼和反碼了,我們來仔細觀察一下反碼的計算方法,對一個負整數位數爲w的二進制數-n來說,它的反碼錶示爲(2w-1-n),對比補碼的表示2w-n,我們能夠發現補碼=反碼+1。
我們很快就能推出對於一個負整數來說,我們可以算出它的二進制相反數表示,然後對所有的位取反,最後加一,得到的就是補碼的值。這樣我們就使用反碼成功解決了減法的問題。
當然了,和反碼一樣,上面的表示都是對負整數而言,正整數的無符號碼、原碼、反碼、補碼都是一樣的~
CSAPP給出了補碼的數學公式:
TIM截圖20191105192735.png
同樣的這個公式我們也可以通過證明得到,並且通過這個公式我們也可以直接計算出補碼的實際值。
由此我們能推斷出由此我們能推斷出TMAXw=2w-1-1,TMINw=-2w-1。如八位二進制原碼的範圍應該爲[-128,127]。這也是爲什麼在一般的程序設計語言中,無符號數據類型的負數範圍要比正數大了。
補碼的計算就十分簡單了,直接相加就可以得到答案了~(當然溢出進位依然是要捨去的。),這也是反碼的計算原理。

一點感想

補碼的出現解決了很多問題,出於崇敬我上網搜索了補碼的發明者,結果是。。。。沒有搜索到,並且經過查證,早在1645年,Pascal的計算器用的十進制補碼。後來到了20世紀,經由馮諾依曼引入到了計算機中。
經典的理論與知識總是在流傳中愈加讓人感到先輩們的偉大,想必3個世紀之前的那位大師在使用補碼來解決計算問題時並沒有想到有一天信息時代的崛起會讓他的發現即使到了今天也依舊熠熠生輝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章