從零開始給女朋友講計算機 1 - 從位元、位元組、補碼到 ASCII、GB2312、Unicode

Zijian/TENG發表於2021-07-15

起因

在程式碼 review 的過程中,總是發現有人在資料型別轉換(reinterpret_cast)、大小端等問題(什麼情況下需要考慮大小端,什麼情況下不需要考慮)上犯錯誤,究其原因是沒有徹徹底底地搞懂資料的二進位制表示。我想寫篇文章,用通俗易懂的語言把這件事情說明白,通俗易懂到我希望我的小白女友也能看懂。於是我就嘗試著先做些鋪墊,給她講了些基礎。發現效果出奇的好,於是趕緊把這一過程記錄如下。

0 和 1 的世界

計算機的世界只有 0 和 1,所有的資料都由 0 和 1 的組合:數字、字母、漢字、圖片、音樂、電影、遊戲、網頁等都可以由很多的 0 和 1 組成。

計算機如何知道一長串的 0 和 1 是什麼含義呢?

比如 0100 0001 可能表示數字 65,可能表示大寫字母A,可能和更多的 0 和 1 共同組成一個漢字,也可能表示圖片上某個點的顏色,其意義完全取決於人們約定的規則

位元和位元組

正著說:每一個 0 和 1 叫做一個位元(bit),8 個位元組成一個位元組(Byte)。位元組是計算機的基本單位,通常計算機一次最少處理一個位元組。
例如:人們常說的一個 Word 文件 100 KB,一張圖片 2 MB,一首歌 10 MB,一部電影 4 GB,記憶體 8 GB,硬碟 512 GB 等等。這裡的大“B”就是 Bytes,位元組。
位元(bit)最常見於寬頻的宣傳:例如 500M 寬頻的完整單位是 500 Mbps(注意這裡是小“b”,不是大“B”)。bps 即 bits per second,500Mbps 指的是每秒最大傳輸 500 兆位元(bit)。所以 500M 的寬頻最快下載速度不是 500 MB/s,而是 500/8 = 62.5 MB/s。
反著再說一次:一個位元組(byte)有 8 個位元(bit);每個位元只能是 0 或 1,8 個位元一共有 2^8 = 256 種組合,可以代表 256 種含義(具體含義完全取決於人們約定的規則)。

數字的二進位制表示:用 0 和 1 表示數字

首先想到用 8 個位元表示 0-255:人們約定,高位到低位每個 bit 有不同的權重,代表不同的值,如此便可用 8 個 bit 表示 0-255 的所有數字。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
權重 128 64 32 16 8 4 2 1
舉例:0 0 0 0 0 0 0 0 0
舉例:35 0 0 1 0 0 0 1 1
舉例:65 0 1 0 0 0 0 0 1
舉例:128 1 0 0 0 0 0 0 0
舉例:255 1 1 1 1 1 1 1 1

對於這種不考慮負數的情況,我們稱之為無符號數

那如何表示一個負數(有符號數)?

有很多種方法,只要約定好一個規則即可。比如我們可以約定,最高位 bit7 代表符號位,0 代表正數,1 代表負數。於是一個位元組,8 個 bit 可以表示 -127 ~ 127 的數字。注意其中 0 有兩種表示,+0 和 -0。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
權重 +/- 64 32 16 8 4 2 1
舉例:+0 0 0 0 0 0 0 0 0
舉例:-0 1 0 0 0 0 0 0 0
舉例:35 0 0 1 0 0 0 1 1
舉例:-65 1 1 0 0 0 0 0 1
舉例:127 0 1 1 1 1 1 1 1
舉例:-127 1 1 1 1 1 1 1 1

現實計算機世界的負數幾乎都是補碼表示。和無符號數的規則相比,差別僅在最高位的權重為。於是一個位元組,8 個 bit 可以表示 -128 ~ 127 的數字。其中 0 只有一種表示。

高位 -> 低位 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
權重 -128 64 32 16 8 4 2 1
舉例:0 0 0 0 0 0 0 0 0
舉例:35 0 0 1 0 0 0 1 1
舉例:65 0 1 0 0 0 0 0 1
舉例:-128 1 0 0 0 0 0 0 0
舉例:127 0 1 1 1 1 1 1 1
舉例:-127 1 0 0 0 0 0 0 1
舉例:-1 1 1 1 1 1 1 1 1

先停一下

看到這裡,如果問你,1 0 0 0 0 0 0 0 代表一個什麼數字,你要怎麼回答?千萬別急著回答,回答之前應該先問清楚,要按照什麼規則去解析。比如這串 0/1 表示的是一個無符號數還是一個補碼錶示的有符號數。

如何表示更大的數?比如 10000

用多個位元組表示。一個位元組不夠就兩個,兩個不夠就三個、四個甚至八個十六個,直到夠用!用 2 個位元組就能夠表示 0 - 65535 之間的無符號數,用 4 個位元組就能表示 0 - 4294967295 的無符號數!

高位 -> 低位 bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
權重 32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
舉例:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
舉例:65 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
舉例:255 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
舉例:10000 0 0 1 0 0 1 1 1 0 0 0 1 0 0 0 0
舉例:40256 1 0 0 1 1 1 0 1 0 1 0 0 0 0 0 0
舉例:60666 1 1 1 0 1 1 0 0 1 1 1 1 1 0 1 0

有符號數(補碼)也是類似的,只不過最高位的權重為負。用 2 個位元組就能夠表示 -32768 到 32767 之間的有符號數,用 4 個位元組就能表示 -2147483648 到 2147483647 的有符號數!
直接使用上面的表格(二進位制表示的 bit 15 到 bit 0 和上面一模一樣),但是現在按照補碼的規則進行解析(最高位權重為負),於是得到的結果就不一樣了。

高位 -> 低位 bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
權重 -32768 16384 8192 4096 2048 1024 512 256 128 64 32 16 8 4 2 1
舉例:0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
舉例:65 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
舉例:255 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
舉例:-25280 1 0 0 1 1 1 0 1 0 1 0 0 0 0 0 0
舉例:-4870 1 1 1 0 1 1 0 0 1 1 1 1 1 0 1 0

十六進位制:二進位制的簡化表示法

二進位制要用 8 個 0/1 表示一個 byte,太不方便,為簡化表示,十六進位制用分別用一個 0-F 表示一個位元組的前 4 位和後 4 位。一般還會加上字首0x,以提醒讀者後面是 16 進製表示法。

如何表示帶小數點的數(浮點數)?

還是一樣,只要約定好一個規則就行。計算機界流行的浮點數規則是 IEEE 定義單精度浮點(4 位元組表示)和雙精度浮點(8 位元組表示)。具體規則比較複雜,不在這裡展開。

如何表示字元?

我們可以約定,0000 0001 代表 a,0000 0002 代表 b,以此類推。從 0000 0000 到 1111 1111 的 256 種組合中表示 a-z、A-Z,加上各種標點符號也是綽綽有餘。現實計算機世界幾乎都心照不宣地採用 ASCII 規則來表示常見的英文字元、標點以及一些不顯示的控制字元等。ASCII 只用了 7 個 bit。

進位制 十進位制 十六進位制 字元/縮寫 解釋
00000000 0 00 NUL (NULL) 空字元
00001010 10 0A LF/NL(Line Feed/New Line) 換行鍵
00001101 13 0D CR (Carriage Return) Enter鍵
00100000 32 20 (Space) 空格
00100001 33 21 !
00101100 44 2C ,
00101110 46 2E .
00110000 48 30 0
00110001 49 31 1
00110010 50 32 2
01000000 64 40 @
01000001 65 41 A
01000010 66 42 B
01000011 67 43 C
01011000 88 58 X
01011001 89 59 Y
01011010 90 5A Z
01100001 97 61 a
01100010 98 62 b
01100011 99 63 c
01111000 120 78 x
01111001 121 79 y
01111010 122 7A z
01111111 127 7F DEL (Delete) 刪除

如何表示漢字?

一個位元組一共就 256 種排列組合,就算每個組合代表一個漢字,也只能表示 256 個漢字,這顯然是不夠的。要想表示一個漢字,至少需要 2 個位元組。這樣就有 2^16 = 65536 種排列組合,可以表示 65536 個漢字了。應對常見的漢字已經不成問題。GB2312 編碼就是用兩個位元組給漢字編碼的。具體編碼規則網上找得到,這裡不詳細展開。

如何表示韓文、日文、阿拉伯文等所有字元?

每個國家、地區都有自己的編碼方式。比如同樣的一串數字 1011 0000 1010 0001 在GB2312 編碼下代表漢字“啊”,而在某種日文編碼規則中則可能代表一個日文字元。
比如一個日本程式設計師開發了一個軟體,在日文編碼的機器上可以正常顯示日文,但是如果拿到中文編碼的機器上就會顯示亂碼。為解決這一問題,推出了 Unicode 編碼。Unicode 採用 4 位元組編碼,可以表示 2^32 = 4294967295 個字元,足夠容納目前世界上所有已知的字元了,甚至包括各種 emoji 表情!

總結

計算機的世界由 0/1 組成,數字、字母、圖片等等所有資訊都由一串串的 0/1 表示。8 個位元組成一個位元組,位元組是計算機的基本單位。一個位元組可以表示 2^8 = 256 種含義,如何解析完全取決於人們約定的規則。如果一個位元組不足以表示所有的範圍、可能性,就用多個位元組表示。

相關文章