談談我眼中的補碼
導讀:補碼是如何產生的、計算機如何表示負數。
1.前提認知
(1)計算機中只有加法器,加減法使用的都是加法器,同時計算機通過加法器左移累加實現乘法運算、右移累減實現除法運算。
(2)補碼是一種編碼格式,它不是真實的數字。
我在之前的文章《從電晶體開始聊聊計算機為什麼採用二進位制》中說過計算機中所有機器碼“0101”實際上是高低電平的不同排列組合。
至於這些“0101”數字真正表示的是什麼人類可讀的資訊,是由電腦科學家制定的標準決定的。
這個標準就是我們常說的“編碼格式”或“編碼規則”,比如“0000_0001”在某種標準下表示現實世界十進位制1,但是在另一種標準下可以表示漢字“我”,在另一種標準下表示“A”。
所以,學習原碼、補碼時不要認為它們是數字,它們並沒有表示實際大小的意義。
(3)如下約定:“49-9=40”這個等式中49是被減數,9是減數。
2.補碼的實質
2.1.補碼的引入
假如我們面對的是一臺十進位制的2bit計算機,即這個計算機最多能儲存兩位數。
(1)那麼如何讓9變為0?
第一種方式是9-9,得數為0;
第二種方式是9+91,得數也為0。因為9+91=100,而2bit的計算機無法記錄百位數1,所以得到0。
從這裡可以發現一個規律,對於2bit的計算機來說,-9的計算效果相當於+91。
下面是對“49-9”按照這種計算效果進行計算:
49-9
=49+91
=140
因為3bit的140超出儲存空間,所以要捨去百位的1,得數為40,即49-9=40。
(2)同樣一臺十進位制的2bit計算機,我們可以計算一下“30-20”。
對於“20”這個數字來說,它要想變為0,要麼減去20,要麼加上80,所以“-20”的計算效果與“+80”相同。
接下來計算一下30-20:
30-20
=30+80
=110
捨去最高位1,得數10,即30-20等於10。
從上面的這兩個例子就可以看出對於一臺十進位制的2bit計算機來說,減法可以通過加法來實現。
這裡產生兩個問題:(1)“減數”和替換它的“加數”之間有什麼樣的對應關係,即如何根據減數確定加數;
(2)為什麼減法可以使用加法替代。
2.2.加數是減數的補碼
對於“49-9=49+91”而言,減數9加上91正好為100,同樣“30-20=30+80”中減數20加上80也是100。
這不是巧合,而是一種計算方式。
對於一臺十進位制的2bit計算機,這裡面和減數一起加起來得到100的那個數就是減數的“補碼”,減法計算其實就是加上那個減數的補碼。
2.3.為什麼減法可以使用加法替代
在回答這個問題之前,先搞清楚對於一臺十進位制的2bit計算機,為什麼要以100為補碼計算的標準呢?
原因很簡單,因為2bit計算機只能儲存2位數,100這個3bit數字實際上就是00,數字當加到100時,自動減少100。
換句話說,對於2bit的十進位制計算機來說,遇見100就清零,我這裡簡記為“遇百變零”,這就是一個減法,只不過是一次就固定的減100。
計算機可以藉助這個特性實現減法。
比如“30-20”是在30的基礎上減掉20,我們“遇百變零”是減100,而此處只要減去20,多減去100,那麼我們就加上80。
所以“30-20=30+80”。
這就是為什麼減法可以使用加法替代。
進而也可以認識到,減數與其補碼兩個數相加之和會將所有位數清零。
2.4.減數補碼的計算
上面已經說過:減數與其補碼兩個數相加之和會將計算機所有位數清零。
這就是減數的補碼的計算方式。
對於十進位制計算機來說,2bit計算機,減數 1的補碼就是99;4bit計算機,減數1的補碼是9999。
對於二進位制計算機來說,2bit計算機,減數01的補碼就是11;4bit計算機,減數0001的補碼就是1111.
到了這裡,我們用二進位制驗證一下,4bit計算機計算4-3。
3的二進位制是0011,3作為減數,所以要變為補碼與4相加,那麼我們先計算一下0011的補碼,如下表。
減數 |
|
0 |
0 |
1 |
1 |
補碼 |
|
1 |
1 |
0 |
1 |
清零 |
1 |
0 |
0 |
0 |
0 |
減數的二進位制數與其補碼之和會將4bit清零,可以看出0011的補碼是1101,也就是減數3參與加法器計算時使用的是“1101”編碼。
所以“4-3”在計算機內就是“0100+1101”,結果為10001,最高位無法儲存、捨去,結果為0001,如下表。
4的補碼 |
|
0 |
1 |
0 |
0 |
-3的補碼 |
|
1 |
1 |
0 |
1 |
和 |
1 |
0 |
0 |
0 |
1 |
即4-3的計算結果為0001,轉換為十進位制,就是1。
到這裡,我們暫停,先回憶一下:計算機內的減法計算實質上是被減數加上減數的補碼。比如49-9=49+91=40.
對於N位(bit)計算機而言,一個減數與其補碼之和可以使N位清零。
減數就是我們現實生活中人類可讀的數字,補碼是計算機識別的編碼符號。
由減數轉化為補碼這個過程稱為編碼,如實際數字-9轉換為補碼91就是編碼;
由補碼轉換為減數這個過程稱為解碼,如補碼91轉換為實際數字-9就是解碼。
3.補碼如何表示負數
本篇文章的兩個知識點,補碼的計算已經說完了。
下面說下一個知識點,補碼錶示負數的規律。
3.1.補碼一分為二
因為計算機內沒有減號、負號,科學家為了表示負數,就將數字區間一分為二,一半表示正數,另一半表示負數。
同樣以十進位制的2bit計算機作為說明,2bit計算機能夠儲存00到99一共100個數字。
不對!應該是00到99一共100個補碼。
計算機識別的不是人類可讀資訊,而是這些可讀資訊轉換的編碼,一定要有這種認知!
科學家將00到49這50個補碼作為正數,而且和實際數字一一對應,比如00表示實際的0,49對應實際的49。
之後將50到99這50個補碼作為負數,但不是50表示實際數字-1,而是99表示實際數字的-1,50表示實際數字-50。
負數和補碼之間之所以採用這種對映方式,還是因為“補碼”這種編碼格式:負數的絕對值(也就是減法中的減數)與其補碼之和會使計算機所有位數清零。
比如-1的補碼就是99,-50的補碼是50。
反過來想(也就是解碼),補碼99表示實際數字-1,補碼50表示實際數字-50.
總之,不論編碼還是解碼,使用的都是補碼的規則:負數絕對值與補碼之和清零所有位。
到這裡你可能想明白了為什麼8bit二進位制計算機內補碼1111_1111表示-1,為什麼補碼1000_0000是最大負數-128而不是-0了吧。
這樣一來,00到99這100個補碼就表示了實際數字-50到49。
二進位制計算機也是如此,比如8bit計算機共有0000_0000到1111_1111一共256個補碼。
256個補碼一分為二,0000_0000到0111_1111共計128個補碼錶示正數,而且和實際數字一一對應。
比如補碼0000_0000表示實際數字0,0000_1000表示實際數字8,0111_1111表示實際數字127.
1000_0000到1111_1111共計128個補碼錶示負數,和實際負數之間採用補碼這種對映關係。
比如補碼1111_1111表示-1,因為1111_1111加上0000_0001清零;補碼1000_0000表示-128,因為1000_0000加上1000_0000清零。
如此一來,8bit二進位制計算機就可以表示-128到127這些實際大小的數字了。
在計算機中,8位帶符號二進位制數的取值範圍是[-128, 127],比如Java中byte型別。
這與我們上面的解釋對應上了。
同時也澄清了一個誤會,計算機最高位1表示負數,是因為1000_0000將256個補碼恰好一分為二,用1000_0000及以後的補碼錶示負數。
計算機對於最高位是1的數字並不是將首位1直接變為負號,而是通過補碼這種編碼將其整體解碼為負數。
所以不要將最高位的1與其它位的二進位制數分離開來,分別看待。
3.2.補碼的解碼
在之前的案例中,我們實現減法都是大數減去小數,如49-9,3-20,4-3.但是如果換成小數減去大數,好像就出問題了。
如果你沒有發現可以計算一下“9-49”。
9-49
=9+51
=60
竟然出現了“9-49=9+51=60”的情況?
9-49應該等於-40,怎麼是60呢?
原因很簡單,60是-40的補碼。
計算機只識別補碼60,之後就會解碼將其轉化為實際數字-40。
這時你可能想到了文章開篇說的一種認知——補碼只是一種編碼格式,它不是表示真正大小的數字。
此時60這個編碼對應的實際數字就是-40,即9-49=-40。
到了這裡,我們就瞭解了人類是如何給計算機賦予負號的含義。就是通過補碼的編碼與解碼。
4.總結
4.1.運算資料都是補碼形式
人類輸入的運算資訊在計算機中是以補碼的形式存在的。不論是加法中的加數,還是減法中的被減數、減數,還是運算結果。
你可能想到了“49-9=49+91=40”中的“40”其實也是補碼,只不過形式上和實際數字相同。
4.2.減法的執行過程
(1)減法中的被減數和減數首先按照補碼這種編碼格式轉換為補碼,被減數和補碼從形式上看一模一樣,減數則是與其補碼之和清零。
(2)加法器執行加法。
(3)如果資料溢位,那麼就將剩下的資料解碼為實際數字;如果沒有溢位,就將這個結果解碼為實際數字。
以十進位制2bit計算機為例,49-9轉換為補碼49和91,之後加法器相加,溢位一位得到補碼40,解碼為實際數字40;
9-49轉換為補碼是9和51,之後加法器相加,沒有溢位得到補碼60,解碼為實際數字-40。
以上就是我對補碼的初步認識。