為什麼電腦用二而不用十來數數
從前面幾階你已經知道,電腦是由開關打造出來的——一個個不是開就是關、不是導通就是不導通的微小 電晶體。一個開關恰好只有兩個可靠的狀態,而要它去保持十個不同的電壓位準來代表數字 0 到 9,就像要人在昏暗房間裡分辨十種灰階:理論上做得到,實務上卻脆弱又容易出錯。所以我們只用兩個狀態,並把它們命名為 0 與 1。每一個這樣「開或關」的答案就是一個 位元(bit),一切資訊的原子。
二進位不是另一種算術,只是手指頭的數目不同罷了。在我們日常的十進位裡,每一位的值都是它右邊那一位的十倍:1、10、100、1000。在 二進位 裡,每一位的值是右邊那一位的兩倍:1、2、4、8、16……也就是 2 的次方。二進位數 1011 代表一個 8、零個 4、一個 2、一個 1,也就是 8 + 2 + 1 = 11。讀二進位,正是你早已信任的小學位值概念,只是把乘數從十換成了二。
位元、位元組與字組
單獨一個位元幾乎裝不了什麼——是或否、開或關。真正的資料需要把位元成群打包。標準的一包是八個位元,叫做一個 位元組(byte),可裝 2^8 = 256 種圖樣中的一種。位元組 是多數機器會單獨交給你的最小一塊;它是記憶體定址的單位,也剛好大到能裝下一個普通的文字字元,這並非巧合。位元組之上坐著 字組(word)——某個特定處理器一口想嚼的那一份自然份量,在現代機器上通常是 4 個位元組(32 位元)或 8 個位元組(64 位元)。
這三種尺寸——位元、位元組與字組——是你從這裡開始用來量度一切的尺。字組大小是處理器最深層的特徵之一:它決定了一個位址能有多大(因而決定機器能命名多少記憶體),也決定了暫存器與加法器有多寬。這就是為什麼大家會談 32 位元相對於 64 位元 的機器——差別就在字組的寬度,連帶決定了處理器能直接指向的世界有多大。我們現在不繼續追這條線索,先把這三把尺握好就行。
十六進位:給累壞的人類用的速記
二進位很誠實,讀起來卻累人:單單一個 32 位元字組就是三十二個 0 和 1,一不小心就數錯。十六進位——也就是基底十六——正是解藥。它用十六個數字:0 到 9,然後用 A、B、C、D、E、F 代表十到十五的值。妙處在於十六是 2 的四次方,所以每一個十六進位數字恰好打包四個位元,不多不少。十六進位 並不是另一種儲存數字的方式;機器裡的位元一點都沒變。它純粹是同一批位元更友善的記法,就像「一打」比「十二個一」更友善一樣。
因為四個位元對應一個十六進位數字,二進位與十六進位之間的轉換根本不必算術——只是重新分組而已。從右邊開始把二進位切成 nibble(四個位元一組),再對著一張你很快就會背起來的小表逐組讀出。圖樣 1111 是 F,1010 是 A,0000 是 0。為了讓十六進位在書寫上不致混淆,我們在前面加上 0x 標記,所以 0x1F 是十六進位,而單寫 31 仍是十進位。
binary: 1011 1110 1110 1111 nibbles: 1011 1110 1110 1111 hex: B E E F result: 0xBEEF (= 48879 in decimal) handy reference: 0000=0 0100=4 1000=8 1100=C 0001=1 0101=5 1001=9 1101=D 0010=2 0110=6 1010=A 1110=E 0011=3 0111=7 1011=B 1111=F
徒手在不同基底之間轉換
基底轉換 有兩個方向值得徹底掌握。二進位轉十進位,就是把有 1 的那些位值加起來——對 1011 來說是 8 + 2 + 1 = 11。反方向的十進位轉二進位,用的是反覆除法:一次又一次除以二,把餘數由下往上讀,就拼出二進位的各位數字。原理是同一套位值邏輯反著跑:每除以二一次,就從最右邊剝下一個位元當餘數。
- 拿十進位數字,比如 13。除以 2:13 / 2 = 6 餘 1。
- 再把商除一次:6 / 2 = 3 餘 0;接著 3 / 2 = 1 餘 1;再 1 / 2 = 0 餘 1。
- 當商變成 0 就停。把餘數從最後一個讀到第一個(由下往上):1、1、0、1。
- 那就是 1101,等於 8 + 4 + 1 = 13。再轉回十進位驗算一遍,確認來回一致。
注意,在二進位與十六進位之間移動時,你完全不必繞經十進位——那段繞路只會招來錯誤。把位元分成 nibble 就完成了。而當這些是 無號整數(沒有負號的單純計數)時,n 個位元涵蓋 0 到 2^n - 1,此外什麼都沒有。那個天花板是真實存在的:一旦衝過頭,數字就會繞回來,這正是緊接著的二補數與溢位兩篇指南要正面迎擊的危險。眼下要帶走的本領,是在二進位、十進位、十六進位之間流暢地移動。
位元不只是數字
這裡有個安靜而深刻的觀念,這一階接下來的內容都倚賴它:一串位元本身並沒有內建的意義。位元組 0x41,若當作無號數來讀是 65,當作文字來讀是字母 'A',當作顏色的一部分來讀則是某種暗紅色。位元本身完全沒有說明該是哪一種。意義全然存在於約定之中——也就是寫位元的人與讀位元的人之間的慣例。這正是為什麼二進位足以表達一切的最深層原因:數字、字母、聲音、影像都化為同一套由 0 和 1 組成的字母表,唯一的區別只在於我們約定如何詮釋它們。
這直接呼應了基礎那一階的 抽象層。在最底層,硬體只是搬動毫無特徵的位元圖樣;意義——數字、字元、指令、位址——是由上面各層所賦予的,由烤進指令集、編譯器與程式裡的種種慣例所賦予。同一套把兩個無號計數相加的機器,只要對完全相同的那批位元改用另一種約定的讀法,就能把兩個有號數相加——這正是下一篇指南用二補數打造出的優雅把戲。