鏈接:https://www.zhihu.com/question/20159860/answer/21113783
來源:知乎
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
---
背景
復習c++的時候遇到二進制編碼問題,上網搜索了一番,終於有點眉目。 一般來說,初學二進制編碼時,會看到如下描述:
原碼表示法是機器數的一種簡單的表示法。其符號位用0表示正號,用:表示負號,數值一般用二進制形式表示。
機器數的反碼可由原碼得到。如果機器數是正數,則該機器數的反碼與原碼一樣;如果機器數是負數,則該機器數的反碼是對它的原碼(符號位除外)各位取反而得到的。
機器數的補碼可由原碼得到。如果機器數是正數,則該機器數的補碼與原碼一樣;如果機器數是負數,則該機器數的補碼是對它的原碼(除符號位外)各位取反,並在未位加1而得到的。
如果是為了考試,死記即可。但我總想搞清楚為什么計算機里面的數要這樣子表達?意義何在?-128的補碼為什么是10000000?為什么補碼有這么奇怪的運算規則?計算機算減法的時候都需要從源碼到補碼的計算嗎?
思路
google了一下,看到了這樣一篇文章,注意到文中關於補碼來歷的描述,可以總結如下:
- 計算機里面,只有加法器,沒有減法器,所有的減法運算,都必須用加法進行。
- 用補數代替原數,可把減法轉變為加法。出現的進位就是模,此時的進位,就應該忽略不計。
- 二進制下,有多少位數參加運算,模就是在 1 的后面加上多少個 0。
- 補碼就是按照這個要求來定義的:正數不變,負數即用模減去絕對值。
補充解釋一下“模”的概念(不准確):
考慮時鍾上時間的計算,假設現在時針指向數字3,若問“6小時前時針指向的數字是幾”,則可以:
1. 將時針逆時針撥動6格。
2. 將時針順時針撥動12 - 6 = 6格。
兩者的結果是一樣的。這里稱12為“模”。
故有 3時 - 6個小時 = 3時 + (12 - 6個小時),這里可以看到將減法轉換成加法的過程,即“加上模減去絕對值的差”。
所以,假設模是10,有效位數為1,當我們計算 9 - 7 的時候:
9 - 7 => 9 + (10 - 7) = 12,去掉最高的位后,得到2,這是正確的結果。
從9 - 7 = 2 和 9 + (10 - 7) = 12可以憑后者比前者多“1”來看出前者中“7”的正負號。
作者的意思是說,計算機里面所有數都以補碼形式保存,加減運算都是補碼之間的加法運算。然后作者提出了一個我之前沒聽過的觀點:
補數 和 補碼的定義式 里面,根本就沒有什么符號位。這最高位的1、0是自然出現的,並不是由人來規定的。
的確,符號位在補碼運算里面是“模”,本身並不帶符號的意義。因為計算機將加法轉換成加上一個“負數”,而負數又以補碼的形式表現。補碼比源碼多一位,從這多出來的一位可以推斷出原來數字的正負號,所以成為了符號位。也可以這樣認為,留出一位(不全部占滿)的原因是要用“模”來表示正負數。
也就是說,不是特意留出一個符號位,用1和0來表示正負號。而是補碼運算可以用最高位來表示正負,所以符號位誕生了。
那么為什么-128的補碼是10000000?可以這樣理解。-128是一個負數,所以它的補碼是它的“模”減去它的絕對值,即:
100000000 - 10000000 = 10000000
那么為什么負數補碼等於源碼的反碼加一呢?可以這樣推導:
100000000 - 10000000
= (11111111 + 00000001) - 10000000
= 11111111 - 10000000 + 1
= 01111111 + 1 //反碼加一
= 10000000
由此我們得知,在計算機里面所有的數字都以補碼形式存儲。127存成01111111,-127存成11111111,算減法就變成算加法了,盡管你看到的是“-”號。
-------
今天讀《計算機組成-結構化方法》后,對這個問題有了新的理解。
將負數用補碼表示,實際上是實現了一種從[-128, 127]到[0, 255]的映射。如下所示:
+----------------------------+
| 255 -1 11111111 |
| 254 -2 11111110 |
| 253 -3 11111101 |
| 252 -4 11111100 |
| 251 -5 11111011 |
| 246 -10 11110110 |
| 236 -20 11101100 |
| 226 -30 11100010 |
| 216 -40 11011000 |
| 206 -50 11001110 |
| 196 -60 11000100 |
| 186 -70 10111010 |
| 156 -100 10011100 |
| 129 -127 10000001 |
| 128 -128 10000000 |
| 127 127 01111111 |
| 100 100 01100100 |
| 70 70 01000110 |
| 60 60 00111100 |
| 50 50 00110010 |
| 40 40 00101000 |
| 30 30 00011110 |
| 20 20 00010100 |
| 10 10 00001010 |
| 5 5 00000101 |
| 4 4 00000100 |
| 3 3 00000011 |
| 2 2 00000010 |
| 1 1 00000001 |
| 0 0 00000000 |
+----------------------------+