计算机只认识 0 和 1,到底是怎么存储文字、图像,甚至是视频的?难道只用 0 和 1 就可以表示任何东西吗?
如何将一个东西用 0 和 1 表示,本质上是如何将这个东西数字化。
考虑一个问题,如果你只能使用数字来表示单词,你会怎么做?
一个很自然的想法是,将所有字母编号,构造出一张数字 ⇄ 字母的「互译表」:
数字 | 字母 | 数字 | 字母 | 数字 | 字母 |
---|---|---|---|---|---|
0 | a | 9 | j | 18 | s |
1 | b | 10 | k | 19 | t |
2 | c | 11 | l | 20 | u |
3 | d | 12 | m | 21 | v |
4 | e | 13 | n | 22 | w |
5 | f | 14 | o | 23 | x |
6 | g | 15 | p | 24 | y |
7 | h | 16 | q | 25 | z |
8 | i | 17 | r |
然后就可以使用 0–25 来写单词了!比如 apple 就是 0 15 15 11 4。
但是,内存的空间是连续的。如果我们把中间的空格删除,就会产生歧义:01515114 可以被理解成 0 15 15 11 4(apple),也可以被理解成 0 1 5 1 5 1 1 4(abfbfbbe)。
最简单的解决方法就是用固定长度的数字表示每个字母。比如,表中最大的数字 25 是两位数,我们加前导零把所有数字都变成两位数,apple 就变成了 00 15 15 11 04。即便去掉中间的空格,也不会产生歧义。
用二进制编码跟十进制编码没有本质区别,前者是 0 和 1 的排列组合,后者是 0–9 的排列组合。
下表是目前计算机上普遍使用的 ASCII 编码,充分利用所有 7 位的二进制数 000 0000 ~ 111 1111 编码了 128 种字符。表格的第 1 行是数字的前 3 位,前 4 列是数字的后 4 位。
![](ascii.png)
![](asciis.png)
![](braille.png)
计算机之外也有编码系统的存在,比如盲文也是一种编码系统,但是盲文没有像 ASCII 编码一样充分利用 6 个点的所有组合。可以观察到,所有字母要不就是左上角有点,要不就是跟左上角相邻的都有点。这是为了让盲人更好地定位。
所以,不同场景设计出的编码系统可能是完全不一样的。ASCII 为了节约昂贵的内存,充分利用了数字的所有排列组合。盲文使用了更多的点,但是增加了阅读体验。
在某些场景,顺序也很重要。用 0 和 1 表示(有限的)自然数也是一种编码,我们可以把所有需要表示的自然数列出来,与二进制数任意地一一配对,比如用 3 位二进制数 000 ~ 111 编码自然数 0, 1, 2, 3, 4, 5, 6 的时候,偏是把 010 对应自然数 0,但是这种乱序的编码方式在做运算的时候就头疼了。
《为什么计算机要使用二进制?》 中提到的加法器可以做 n 位二进制数加法,只要我们按照计数的顺序安排这些自然数,加法器就可以对这种编码下的数字做正确的加法运算。
二进制 | 表示的数字 |
---|---|
000 | 0 |
001 | 1 |
010 | 2 |
011 | 3 |
100 | 4 |
101 | 5 |
111 | 6 |
当把自然数扩展到整数的时候,我们把一半的位置留给负数。为了让负数加法能够复用上面的加法器电路,人们特别地安排了负数的排列。
二进制 | 表示的数字 |
---|---|
000 | 0 |
001 | 1 |
010 | 2 |
011 | 3 |
100 | -4 |
101 | -3 |
110 | -2 |
111 | -1 |
可以发现,负数的顺序有点奇怪,但是每个数字跟它的相反数的二进制编码之和都是 000 或者 1 000(在一个 3 位加法器看来,这也是 000)。比如,001(1) + 111(-1) = 1 000(0)。这种编码负数的方式,就是教科书上所说的补码,可以让加法器不需要考虑正负性,算就完了。
上面都是编码有限个符号的例子。现实世界是连续的,那些不可枚举的物理量怎么用 0 和 1 表示呢?
我们无法用有限个 0 和 1 表示无限的可能性,但是我们可以通过采样,将现实世界中的物理量变成离散的值。这就好比我们用尺子去测量一个物体,总是会估读到毫米,这无疑是不精确的,但是我们能够接受这种误差。
在计算机中也是这样表示颜色的。我们常见的 RGB 颜色模型将颜色分为红(Red)、绿(Green)、蓝(Blue) 3 个分量,分别描述这个颜色有多红、多绿和多蓝。每个分量有 256 种取值(二进制数 0000 0000 ~ 1111 1111),3 个分量放在一起就可以表示颜色。
比如,红色可以表示为 11111111 00000000 00000000,换算成十六进制就是 FF0000(你可能在很多软件中都见过这种表示方式)。在 RGB 颜色模型里,一个颜色从最「不红」到最「红」只有 256 个级别,但这对屏幕显示来说已经足够了。
当然,有很多目前还很难采样的东西,比如气味;又或者是,有时我们无法接受采样带来的误差,比如 π 的精确值……这些就没有办法用有限个 0 和 1 表示了。