文章來源 : https://blog.csdn.net/zhuozuozhi/article/details/80896838
二進制負數的在計算機中采用補碼的方式表示。很多人很好奇為什么使用補碼,直接使用原碼表示多好,看上去更加直觀和易於計算。然而事實告訴我們,這種直觀只是我們人類的一廂情願罷了,在計算機看來,補碼才是它們最想要的。那么,為什么計算機使用補碼更好,又是如何通過補碼來計算數值的呢?
我看過網絡上很多解釋補碼的文章,幾乎一致的回答就是符號位不變,其他各位逐位求反再加一。在此我想說,這些都不是根本原理。誰都知道這么求,數電第一章就明確寫了怎么求,關鍵是為什么這么算,其中的原理是什么?
本文主要的內容就是深入講解補碼的原理,其中內容有相互引用成分及計算機基礎要求,不適合初學者閱讀。當然,隨便看看無所謂啦。
1.什么是補碼
這個沒有找到官方定義,只進行個人定義。
個人定義:補碼是計算機中用來表示負數,使得負數能夠使用加法器參與加法運算的一種碼。
加減是計算機中最常用的運算,加法一般使用加法器來實現,減法則使用減法器實現。那有什么辦法可以將減法變為加法,這樣就可以讓系統只實現加法即可,答案就是補碼。
理解補碼最簡單的例子就是時鍾。
例1:
假如一個時鍾現在顯示的是10點鍾,如何將它調到6點鍾?
解:有兩種方法,一是向后撥8個小時,二是向前撥4個小時
在這個例子中,8 和 4 互為補數,也就是說4的補碼是8,8的補碼是4,而這個時鍾的模就是12
注意:可能有人會想,在往后調8個小時雖然也調到了6點,但是他實際上比原來日期多了12小時。是的,的確如此,但是你的時鍾有地方存儲了這多余的12個小時嗎?答案是沒有,所以在你調完后,你沒有記錄這12個小時,換句話說,你把這溢出的12個小時自動舍棄了。當第二個人來查看鬧鍾時間的時候,他看到的時間就是准的。
例2:
一個數的數值是11,他的模是16,那么他的補碼是多少?
解:16-11 = 5,即補碼就是5。
2.模
關於模沒有找到固定的定義,簡單來說模就是一個循環的周期,在例1中,時鍾的一個周期就是12,所以模是12。一周的模是7天,一天的模是24小時。模是補碼的一個重要概念。
3.使用補碼運算
例3
模為32,使用補碼運算該算式16-13,13 - 16。
解 16-13 = (16 + (32-13))% 32 = 35 % 32 = 3
13-16 = (13 + (32-16))% 32 = 29 % 32 = 29
4.使用補碼進行二進制運算
有看過數電基礎的都應該記得,第一章就有說明二進制補碼是如何運算的。正數的補碼即為自己,負數的補碼為符號位不變,其余逐位求反再加1。
使用該定義,先通過例子求出數值,再對例子進行詳細講解,為什么可以使用負數的補碼來運算。
例4 通過二進制求15-11的值
要想讓減法變加法,必須轉換算式為 15 + (-11)
15為正數,符號位為0,二進制表示為 01111
-11為負數,符號位為1,負數原碼為 11011 ,補碼符號位不變,其余逐位求反再 +1,即 10101
所以 15 + (-11) = 01111 + 10101 = 100100 舍棄第一位溢出位,即00100,即+4
這是一個最簡單的補碼算法運算的例子,卻有很多不解之處。
1.為什么負數 (-11 )逐位求反再+1就可以代表原來的數?
2.為什么第一位舍棄
3.為什么符號位能夠參與運算
先看問題1,-11 先不考慮符號,觀察11的二進制表示,11使用二進制表示是 1011,將1011 逐位求反 得到 0100,因為是逐位求反,1011 +0100 = 1111 ,而1111 + 1 = 10000,發現了什么?10000是四位2二進制數的模。為什么10000是4位二進制數的模呢?原理也很簡單,4位寄存器最高能表示什么數?即 (二進制)1111 = (十進制) 15 , 15 +1 =16 = (二進制)10000,后四位寄存器清0,所以模為16。
所以不管幾位二進制數,取反后得到的值加原碼會剛好的到所有的位都是1的二進制數,再加一就剛好進位得到模。所以取反加一是無論如何都能取到補碼的。比如 :(原)101 + (補)((反)010 +1) = 8 ,(原)10 + (補)((反)01 +1)= 4。
所以,負數符號位不變,其余逐位求反 +1 只是算出補碼最簡單的方法,而不是理論基礎。
前面提到過,使用補碼代替原碼,計算后模掉溢出得到數值就可以得到計算的值了。
再看問題2,為什么高位舍棄,這個問題其實在例1中已經做了說明,因為你沒有存儲這個高位的空間,用最簡單的解釋來解釋就是,一個4位的寄存器,只能存儲數據的后四位,第一位進位沒地方放,就像例1中多出來的12小時,沒有地方存儲,那么就丟棄了,反正結果正確就行。可能有人會問,你說丟棄就丟棄嗎,丟了你怎么保證是正確的?
重新再看一次例4,現在考慮符號位,將符號位加入運算。
15 - 11 = 15 + (-11),其中 -11 為 11011(加了一位符號位,所以模為 16 * 2^1) 他的補碼為 10101 = 21
所以 15 -11 變為 15 +21 = 36 (01111 + 10101) ,原先是減一個數,變換后卻變成了一個比減數更大的加數了,能不溢出嗎。所以要模掉溢出位 36 % 32 = 4 。仔細觀察被模數36 = 1 00100 你會發現,在第二位之前的所有高位都是32的倍數,所以用32進行模運算就會全部清0,這就是為什么高位可以直接舍棄的原因,因為高位永遠會是模的倍數,在模的過程中會被清0。
最后看問題3,為什么符號位能夠參與運算,可能在這前面一直被符號位困擾,為什么參與運算就沒有問題,看了這里的解釋,我相信你講不會再被困擾。
符號位對於我對二進制補碼的理解產生了很大的阻礙,我不明白為什么符號位可以參與運算,而且算出來還是對的。花了不少時間理解了符號位的含義。
首先放棄符號位就是計算機表示專門定義出來表示正數和負數用的這個思維,換成這樣一種理解思路:我們觀察計算機中一個二進制數,它沒有專門的符號位,但是正數和負數在計算機中存儲第一位二進制數會存在差異,如果它的第一位是0,那么他就是正數原碼,如果它的第一位是1,那么他就是負數補碼,我們通過觀察第一位是0還是1即可知道這個數是正數還是負數。
通過以下例子,可以發現符號位其實是算出來的。
例5:計算機運算 9 - 12
解:9 轉五位二進制為 01001 //模為32(100000)
因為12是減數,所以將12轉為補碼變為加數,方便參與加法運算
12 = 01100(原) = 10011 + 1 = 10100(補)
01001 + 10100 = 11101
11101% 100000= 11101
從結果看,我們可以發現,第一位為1,第一位如果是1,那么這個數值是負數補碼,由此可知,這個數是一個負數,只是在計算機中,他表示為11101,反求原碼,即可得到 (11101 - 1) 反 = 00011 = 十進制數 3,由此可知,11101表示的是負數的3
從這個例子當中可以看到,在運算中完全沒有考慮符號位,僅用單純的加法運算進行運算,而得到的結果11101也沒有人為去添加一個符號位,符號位是計算所得的,所以他才能參與運算。我們應該反過來看符號位,正是在補碼運算中,存在第一位為0則是正數,為1則是負數這個規律,我們才方便地認定第一位為符號位,符號位跟人為定義沒有任何關系(人為定義的符號位不可能滿足數學規則參與數學運算)。
補充:在例5中可以發現,我們在第一位之前補了一個0,將四位二進制數補成五位二進制數來計算,為什么要補這個0呢?為了說明這個問題,我們來算一個錯誤的運算
例6:計算機運算 0 - 12
解 : 我們僅僅用四位二進制數(四位寄存器)來算
12 = (二進制)1100 12轉為二進制后是一個四位二進制數,它的模為10000
因為12是減數,所以將12轉為補碼變為加數,方便參與加法運算
12 = 1100(原) = 0011 + 1 = 0100(補)
0000 + 0100 = 0100 = 4 ,第一位是0,說明是正數原碼, 即運算結果為 +4
4 != -12 運算出錯
通過結果發現,只用四位二進制數運算,結果是錯誤的,為什么會產生這種錯誤呢?因為寄存器位數不夠,四位寄存器只能存儲 0 -15 的數值,沒有多余的寄存器位置用來表示這個數的正負,我們雖然算出了符號位,但是計算后所得到的符號位卻沒有地方存放,導致符號位缺失,我們又誤將四位二進制數結果的第一位當做符號位來判斷數的正負,才會出現錯誤的結果。
通過例5我們可以知道,符號位是計算后得出來的,所以我們要預先要在寄存器第一位留出一個位置存放計算后得出的符號位,所以我們先在第一位前面補一個0占位。
由此可知: 要想計算結果正確, 需要滿足 : 寄存器位數 >= max(操作數1,操作數2,....) 的二進制位數加一位
最后,附上一張五位二進制數運算轉換關系圖,自己體會二進制數如何帶着所謂的符號位在計算機中運算:
有如下幾點:
1:五位二進制數的所有正數都在圓環的黑線上,而負數都在圓環的紅線上,如果在黑線上,那么六位寄存器第一位必為0,而在紅線上,那么第一位必為1,我們也是通過這個位來判斷數值的正負
2:五位二進制數運算結果取模后如果在黑線上,那么為正數,否則為負數
3:嘗試寫幾個數字轉為五位二進制數或者寫幾個算式對號入座,觀察是否符合,加深理解
到這里補碼的原理就差不多講清楚了,無非是使用模進行轉化,但是我們站在十進制的角度,去看二進制的東西,的確會顯得相當費解。最后我想說,能發明補碼來計算的數學家真的是太厲害了。
內容有相互嵌套成分,閱讀后回顧前面的內容可能會更加深入理解。
還有很多特殊情況沒有考慮,只講解了最核心的內容。
本文純屬個人理解,如有紕漏,請勿拍磚。