從零構造一臺計算機——二進位制

kaiux發表於2021-08-06

二進位制的定義

人類發明0,1,2,3,4,5,6,7,8,9這10個數字,大概率是因為我們有十個手指。假如我們把這種計數方式稱為十進位制計數法,然後我們來到了一個世界,那裡面的人只有2個手指。我們該如何用十進位制的思想去用創造出二進位制呢。

簡單約定一下,後面用下標表示進位制,比如\(10_2\)表示二進位制,而\(10_{10}\)表示十進位制。

首先,我們能用的數字只有0,1這2個,忘掉十進位制中其餘的數字。那麼我們如何計數呢?以數蘋果為例,在十進位制中,我們數到第\(9_{10}\)個蘋果的時候,我們需要做一個操作就是進位,然後低位置為0。現在來到二進位制,我們如何表示下面蘋果的數量:

需要注意的是,二進位制中只有0,1,當我們數到第\(1_2\)個蘋果的時候,我們需要做的也是進位。類似地,我們會得到\(10_2\),繼續數下去,就是\(11_2\),所以答案就是這裡有\(11_2\)個蘋果。

對於一個十進位制數,我們知道它有個位十位百位千位萬位……,其實這些代表的就是\(10^n\)。比如個位\(10^0\)十位\(10^1\)百位\(10^2\),對於一個數\(123_{10}\),我們可以說它的個位是3,十位是2,百位是1,那麼我們可以通過這樣計算來得到:

\[123_{10} = 1 \cdot 10^2 + 2 \cdot 10^1 + 3 \cdot 10^0 \]

於是,對於十進位制,我們可以抽象出下面的公式:

\[x_{n}x_{n-1}x_{n-2}...x_{0}=\sum_{i=0}^{n}{10^i \cdot x_i} \]

那麼,對於二進位制,我們自然而然可以得到下面的公式(需要注意的是,下面公式右邊的10其實是\(10_2\)):

\[x_{n}x_{n-1}x_{n-2}...x_{0}=\sum_{i=0}^{n}{10^i \cdot x_i} \]

公式右邊的10我們稱之為基底,十進位制的基低是\(10_{10}\),二進位制的基低是\(10_{2}\)

二進位制轉十進位制

對於二進位制,我們已經得到這樣的公式:

\[x_{n}x_{n-1}x_{n-2}...x_{0}=\sum_{i=0}^{n}{\left(10_2\right)^i \cdot x_i} \]

由於二進位制中的0,1和十進位制中的0,1的含義是一樣的,並且二進位制中的\(10_2\)等價於十進位制中的\(2_{10}\),而\(x_i\)(公式右側)在二進位制中只有可能是0,1,所以我們可以作如下推導:

\[\sum_{i=0}^{n}{\left(10_2\right)^i \cdot x_i} \Rightarrow \sum_{i=0}^{n}{\left(2_{10}\right)^i \cdot x_i} \]

於是我們可以用十進位制的方式去表達二進位制:

\[\left(x_{n}x_{n-1}x_{n-2}...x_{0}\right)_{2}=\sum_{i=0}^{n}{\left(2_{10}\right)^i \cdot x_i} \]

我們可以驗證一下:

\[11_2=1\cdot2^1+1\cdot2^0=3 \]

所以二進位制的\(11_2\)等價於十進位制的\(3_{10}\),他們都表示上圖中蘋果的數量。

二進位制的加法運算

二進位制的加減法運算,也可以用十進位制的思想來做,首先通過上面例子我們知道\(1_2\)個蘋果加上\(1_2\)個蘋果等於\(10_2\)個蘋果,也就是說在二進位制中1+1=10。基於這個條件,我們分別來計算1001+01011011+0111

首先看1001+0101,我們列出下面的式子,然後用十進位制加法的思想去做,可以得到結果是1110

\[\begin{split} \left(進位\right)0\quad&0\quad0\quad1\\ &1\quad0\quad0\quad1\\ +&\underline{0\quad1\quad0\quad1}\\ 0\quad&1\quad1\quad1\quad0\\ \end{split} \]

然後來看1011+0111,我們也可以列出下面的式子,用同樣的方法去做,但是這邊最後發生了進位,所以結果是10010

\[\begin{split} \left(進位\right)1\quad&1\quad1\quad1\\ &1\quad0\quad1\quad1\\ +&\underline{0\quad1\quad1\quad1}\\ 1\quad&0\quad0\quad1\quad0\\ \end{split} \]

我們可以轉成十進位制來驗算一下,結果是沒問題的。那麼減法也可以用類似的思想來做。

計算機中二進位制的表示

由於二進位制只能由0,1組成,那麼對於\(n\)位的二進位制數,我們有\(2^n\)中組合:從\(000...0\)\(111...1\)。所以一個\(n\)位二進位制數的表示範圍是\(0 \to 2^n\)(這裡的\(2^n\)是十進位制)。

那麼如何表示負數呢?可能有人會說前面加個-號,和我們平時表示的方式一樣。

其實通過前面的課程我們知道,我們用電路的通斷來表示1或者0,電路連通的時候為1,斷開的時候為0,只有這兩種狀態,沒有第三種。所以-號其實是無法在計算機中表示出來的。

為此,前輩們想出了有符號數,即把二進位制的第一位拿出來作為符號位,1表示負數,0表示正數,剩下的來表示具體的數值。

但是如果負數僅僅用第一位符號位來指示,可能還會遇到很多麻煩:比我們知道十進位制中\((-3)+3=0\),這時候我們轉化為4位的二進位制(第一位為符號位)可以得到\(1011_2+0011_2\),結果是\(1011_2+0011_2=1110_2\),如果\(1110_2\)我們仍然把第一位解釋成符號位,那麼轉換成十進位制,就得到了\((-3)+3=-6\),這顯然是不對的。

那麼如何讓\(1011_2+0011_2=0000_2\)呢?在上面加法的例子中,我們還記得\(1011+0111=10010\),這裡發生了進位,也就是2個四位數的二進位制相加,得到一個五位數的二進位制數。那麼在計算機中,假如我們設計的計算機只能表示4位的二進位制數,那麼最終的結果也只能有4位,也就是在該計算機中,我們會得到\(1011+0111=0010\)(最高位1丟棄了)。

基於這個特性,我們可以這樣設計負的二進位制數,假設這裡計算機只能表示4位二進位制數,並且\(0011_2\)的負數設為\(x\),我們需要得到\(0011_2+x=0000_2\),又因為該計算機只能表示4位數,所以在該計算機中,\(0000_2=10000_2\),注意後面是一個5位二進位制數。所以,我們可以這樣去設計\(x\),使得\(0011_2+x=10000_2\),得到\(x=1101\)。我們再來驗算一下:\(0011_2+1101_2=10000_2 \Rightarrow 0000_2\),於是我們可以說,在該計算機中\(0011_2+1101_2=0000_2\)是成立的。

補碼

上面這種二進位制的表示法,我們就稱之為補碼表示法。

總結一下,可以得到如下的定義(假設計算機能表示的二進位制數為\(n\)位):

\[x_補=\left\{ \begin{aligned} x & & (x\ge0) \\ 2^n & - x & (x<0) \end{aligned} \right. \]

最高位的含義

我們現在知道了補碼的定義,那麼在補碼中,任何符號位為0的整數都能得到符號位為1的負數嗎?我們可以簡單證明一下:

假設有\(n\)位的二進位制數\(x_nx_{n-1}x_{n-2}x_{n-3}...x_2x_1\),且最高位為符號位,所以其數值部分為\(x_{n-1}x_{n-2}x_{n-3}...x_2x_1\)。我們設\(x_{n-1}x_{n-2}x_{n-3}...x_2x_1\)\(t\),那麼\(-t\)可以表示為\(2^n-t\),而\(2^n \Rightarrow 100...0\left(n個0\right)\),所以我們可以列出下面的式子:

\[\begin{split} &1_{n+1}\quad0_{n}\quad0_{n-1}\quad0_{n-2}\quad...\quad0_3\quad0_2\quad0_1\\ -&\underline{ 0_{n+1}\quad0_{n}\quad x_{n-1} \quad x_{n-2} \quad... \quad x_{3}\quad x_2\quad x_1}\\ &r_{n+1}\quad r_{n}\quad r_{n-1} \quad r_{n-2} \quad... \quad r_{3}\quad r_2\quad r_1\\ \end{split} \]

我們設結果為\(r_{n+1}r_nr_{n-1}r_{n-2}r_{n-3}...r_2r_1\),當\(t\)不為0的時候,不管是哪一位不為0,我們做減法的時候肯定會向前借位,但是因為\(2^n\)只有第\(n+1\)位為1,所以只能向前傳遞,借第\(n+1\)位的1。但是因為\(t\)中的\(n+1\)位恆為0,所以我們可以得到\(r_{n+1}=0\),同理\(r_{n}=1\)。所以對於任意的符號位為0的正數,其補碼都是符號位為1的負數。

這裡面有個特殊情況,對於0來說,是不分正負的,而且\(0+0=0\),那麼0到底是+0還是-0呢,也就是說0的最高位應該是什麼?

我們先來看+0,由於+0所有位都是0,所以\((+0)+(+0)=(+0)\),是沒有問題的。但是對於-0來說就不是這樣了,由於-0的最高位為1,所以當\((-0)+(-0)\)之後,最高位變成了0,並且發生了進位,並且進位被丟棄(前面講過),於是我們得到\((-0)+(-0)=(+0)\),這顯然不太對,直覺告訴我們應該用+0來表示0

那麼當最高位為1,其餘位都是0的時候,這個數字代表什麼呢?

還記得我們得出補碼的過程嗎,我們是用\(2^n-x\)算出來的。那麼對於最高位為1的\(n位\)二進位制數來說,其我們可以用十進位制數\(2^{n-1}\)來表示他的值,通過計算\(2^n-2^{n-1}\)我們可以得到他的正數表示也是\(2^{n-1}\),所以當一個\(n\)位二進位制數的最高位為1,其餘位全是0的時候,他的值就是\(\left(-2^{n-1}\right)\)

以一個4位二進位制數為例:

正數 負數
0   0000
1   0001 1111   -1
2   0010 1110   -2
3   0011 1101   -3
4   0100 1100   -4
5   0101 1011   -5
6   0110 1010   -6
7   0111 1001   -7
1000   -8

用加法代替減法

前面補碼的定義可知,一個\(n\)位的二進位制正數\(x\)的負數我們可以用\(x_補 \Rightarrow 2^n - x\)來表示。假如我們要計算\(t-x\),我們可以作如下推斷:

\[\begin{split} & t-x \\ \Rightarrow & t+(-x) \\ \Rightarrow & t+x_補 \end{split} \]

那麼當\(x\)是負數的時候呢?類似地,我們可以得到:

\[\begin{split} & t-x \\ \Rightarrow & t+|x| \\ \Rightarrow & t+x_補 \end{split} \]

這裡的\(|x|\)表示\(x\)的絕對值,我們知道,當\(x\)為負數的時候,其絕對值就是他的補碼。

綜上所述,不論\(x\)是正的還是負的,我們都可以用其補碼來實現用加法代替減法的操作。

總結

最高位作為符號位是一個很巧妙的設計,對於\(n\)位的二進位制數\(x_nx_{n-1}x_{n-2}x_{n-3}...x_1x_0\)來說,他們的數值部分都是\(x_{n-1}x_{n-2}x_{n-2}...x_1x_0\),相當於正好把取值範圍一分為二,一半給了正數,一半給了負數。

補碼的引入也非常合理,最高位不僅可以作為符號位表示數字的正負,並且也可以參與計算、實現用加法代替減法。

相關文章