1.背景
前一段時間在做模型外殼提取程式時候,用到了補碼的知識,現在系統的總結下原碼、補碼、反碼的知識。
你可能注意到了我寫的順序是補碼在反碼前面,是的,補碼不是必須依賴于反碼才能得知的,但本文為了友善講解和容易了解仍然采用了先反碼再補碼的順序。
本文以1個位元組的存儲空間為例進行講解。
2.原碼
2.1.概述
原碼比較簡單,最高位為符号位(1代表負數,0代表正數),其他位表示該數值的絕對值,例如,
對于1個位元組的存儲,1000 1101 代表-13, 0000 1101代表13。
2.2.分析
計算機運算器中有加法運算器,而沒有減法運算器,那麼對于原碼情況,我們結合加法的場景進行分析,
13+2=15,即
0000 1101 + 0000 0010 = 0000 1111 = 15,沒問題,
2+3=5,即
0000 0010 + 0000 0011 = 0000 0101 = 5,也沒問題,
2 + -1 = 1,即
0000 0010 + 1000 0001 = 1000 0011 = -3,?不對!
-2 + -1 = -3,即
1000 0010 + 1000 0001 = 0000 0011 = 3,?不對!
可以看出,對于正數加法沒問題(這也是後續要講的為什麼正數的反碼和補碼與原碼相同的原因之一),對于負數就有問題了。
那麼根據負數是其絕對值(正數)取反推出了反碼的概念。
3.反碼
3.1.概述
正數的反碼和原碼相同,負數的反碼可以由原碼的符号位不變,其他位取反得來,仍以一個位元組存儲為例,
十進制數 | 原碼 | 反碼 |
2 | 0000 0010 | 0000 0010 |
-3 | 1000 0011 | 1111 1100 |
3.2.分析
對于正數反碼和原碼相同,
13+2=15,即
0000 1101 + 0000 0010 = 0000 1111 = 15,沒問題,
2+3=5,即
0000 0010 + 0000 0011 = 0000 0101 = 5,也沒問題,
2 + -1 = 1,即
0000 0010 + 1111 1110 = 0000 0000 = 0,?不對!
-2 + -1 = -3,即
1111 1101 + 1111 1110 = 1111 1011 = -4,?不對!
那麼再來看,
-2 + -3 = -5,即
1111 1101 + 1111 1100 = 1111 1001 = -6,?不對!
3 + -1 = 2,即
0000 0011 + 1111 1110 = 0000 0001 = 1,?不對!
-3 + 1 = -2,即
1111 1100 + 0000 0001 = 1000 0010 = -2,對了!
1 + -1 = 0,即
0000 0001 + 1111 1110 = 1111 1111 = -0,?也對,但為什麼不是0000 0000,這也是0(+0)啊,
可以看到,
對于正數相加,一如既往的沒問題;對于有負數的情況,有的就有問題了,有問題的好像都應該是結果+1後才是準确的,(二進制計算-0+1=+0,即1111 1111 + 0000 0001 = 0000 0000)反正就是有點問題,
既然都引出反碼來了,好像離解決方案更進一步了,有規律了;那能不能引出個其他的什麼碼,對于正數來說這種碼和原碼相同(我們暫且稱之為解碼),
對于負數來說,解碼是其反碼+1,剛剛好能使負數參與的加法計算準确?
那麼到這裡你可能要說了,
上面-2 + -3 是兩個負數相加,如果每個負數都加1,那麼結果應該是在反碼運算結果:-2 + -3 = -6的基礎上進行修正(+1+1=+2),這樣不應該變成-4了嗎?
嗯,好問題,也比較嚴謹,-4是負值的解碼(反碼+1),其對應的反碼應相應的再-1,即反碼為-5,明白了沒?實質上還是反碼間的運算,隻不過封裝了一層,便于計算。
上面-3 + 1 = -2 本來就正确了,如果每個負數都加1,那麼會出現什麼情況?(-3+1) + 1,相應的結果應該等于-2 + 1 = -1了,這不是又出錯了嗎?沒出錯,和上面一樣-1是是負值的解碼,其對應的反碼為-1-1=-2,依然正确,明白了沒?實質上就是反碼間的運算,隻不過便于運算和了解又封裝了一層概念:解碼(暫時的名稱)。
上面3 + -1的情況又是怎樣的呢?3 + -1應該是2,但上述反碼計算後變為了1,不對了;在引入解碼後,3 + (-1 + 1)對應的結果為 1 + 1 = 2了,2也是解碼,正數的解碼等于其反碼,即結果轉換為反碼依然是2,沒問題,又印證了上述反複陳述的論點:實質上就是反碼間的運算,隻不過便于運算和了解又封裝了一層概念。
問題終結者來臨:真的有剛才的解碼嗎?有的,那就是 補碼,
4.補碼
4.1.概述
正數的補碼和原碼相同,負數的補碼是其反碼+1,或者不依賴于反碼的解釋:負數的補碼等于其原碼自低位向高位周遊,尾數的第一個‘1’及右邊的‘0’保持不變,左邊的各位按位取反,符号位不變。
十進制數 | 原碼 | 反碼 | 補碼 |
2 | 0000 0010 | 0000 0010 | 0000 0010 |
-3 | 1000 0011 | 1111 1100 | 1111 1101 |
-0 | 1000 0000 | 1111 1111 | 0000 0000 |
0000 0000 | 0000 0000 | 0000 0000 |
???對于0的表示沒問題了?以前無論是原碼還是反碼都有+0和-0,甚至還有-0 + 1 = 0 的奇怪理論,補碼形式隻有一種0,0就是0,沒有正負之分,或者稱其為正數。
趕快再分析下加法,驗證下上面關于分析反碼推出補碼可以解決負數參與的加法問題設想的正确性。
4.2.分析
呼之欲出,
對于正數補碼和原碼相同,隻有正數參與的加法一如既往的正确,不再贅述;下面分析負數參與的加法場景,
2 + -1 = 1,即
0000 0010 + 1111 1111 = 0000 0001 = 1,沒問題,
-2 + -1 = -3,即
1111 1110 + 1111 1111 = 1111 1101 = -3,沒問題,
那麼再來看,
3 + -1 = 2,即
0000 0011 + 1111 1111 = 0000 0010 = 2,沒問題,
1 + -1 = 0,即
0000 0001 + 1111 1111 = 0000 0000 = 0,沒問題,
4.3.原理
為什麼補碼表示的數參與加法都沒問題?
這裡主要以負數為例,因為正數加法一如既往的簡單正确!
十進制數 | 補碼 |
-1 | 1111 1111 |
1 | 0000 0001 |
-2 | 1111 1110 |
2 | 0000 0010 |
看出規律了嗎?負數和其絕對值的二進制相加等于
,也就是負數的補碼和其絕對值的補碼(原碼)互為同餘數,
n + -1相當于n +(
-1)(這裡括起來是因為這裡的-1的補碼并不是計算機中運算器運算時候進行的“減法”,而是負數的二進制補碼與其絕對值的二進制補碼關系是
-其絕對值的二進制補碼,這種關系是固有的,并不需要計算機的運算器去運算,當然運算器也沒有減法運算器!);
這也是
- 為什麼負數補碼的二進制值(補碼沒有符号位的概念,雖然負數的最高位都是1,正數的最高位都是0)要比正數大的原因了,友善進行加法運算(當然高位溢出舍棄);
- 為什麼負數補碼的最高位一定是1,而正數補碼的最高位一定是0的原因了,因為正數和負數的臨界值 1000 0000如果代表正數的話,其值為 ,
而其相應負數的補碼應該為
,這樣豈不是沖突了?而且也不滿足對負數參數的加法按照“加法運算器”運算的要求,是以1000 0000代表
,而正數最大為
1000 0000 - 0000 0001 = 0111 1111 =
;
同時這也是為什麼有符号的一個位元組能表示
,即
的原因了,因為最小的1000 0000是
的補碼,而正數隻能比1000 0000小,也就是最大為
。
想想,一天24小時,一年四季,一周七天,全球24個時區,六十一甲子,周而複始,減既是加,不扯了,你明白就行。
5.總結
充分了解原碼、補碼和反碼的概念和這樣設計的緣由是很重要的,了解深層次原因可以加深了解,大道至簡,為了友善,為了高效。
參考
- 《計算機組成原理》
- https://www.imooc.com/article/16813?block_id=tuijian_wz