8.3 一个数位上需要几盏灯泡
至此,我们看到了表达数字的不同方法。它们之中有的需要的灯泡数量多,有的需要的灯泡数量少。那么所需灯泡数量最少的方法是什么呢?理论上最少又能少到什么程度呢?
一盏灯泡能表达两种不同的符号,那么两盏灯泡就能表达 4 种不同的符号。相应地,三盏就是 8 种。这还不够表达 0 到 9 之间的 10 个不同符号,因此,一个数位上三盏灯泡是不够的。有四盏灯泡的话,就可以表达 16 种不同符号了,当然也就足够表达 0 到 9 之间的 10 种符号。
事实上,早期的计算机,比如 UNIVACI 就是使用了四盏灯泡表达 数值的方法。该方法被称为 excess-3(加三码)(图 8.7)。使用这种方法总共需要 12 盏灯泡就可以表达 0 到 999 之间的数 7。
7Excess-3 中针对 0~9 中的任意一个数 x,9 减去 x 得到的数和表达 x 的四盏灯泡反转后的数是相同的。因为它具有这样的特征,用 Excess-3 来制作减法电路十分方便,这是这种符号得到普及的理由之一。

图 8.7 Excess-3 中,黑圈表示 1,白圈表示 0
从十进制到二进制
然而,四盏灯光明明足够表达 16 种符号的,而只使用了其中的 10 种符号,这总让人觉得有点浪费。有没有更加精打细算的方法呢?
这和十进制的差别在于十进制中一个数位可以表达 10 个符号。既然一盏灯泡只能表达两种不同的状态,那么进位也与之相适应,逢二进一位,这样做会怎样呢?也就是说,不再是个位、十位、百位、千位这样地进位,而是 1 位、2 位、4 位、8 位这样地进位。这就是二进制(图 8.8)。

图 8.8 4 盏灯泡表达 16 种不同的符号
使用二进制后,10 盏灯泡就能表达 0 到 1023 之间的数字了(图 8.9)。1023 等于 1+2+4+8+16+32+64+128+256+512。上一小节讲的十进制中,要表达 0 到 999 之间的数 12 盏灯泡已经是极限了。可见使用二进制后数的表达效率能得到进一步提高 8。
8十进制中的每一位大概需要 3.32 个灯泡即可,即 log(10)/log(2)。

图 8.9 二进制中 10 盏灯泡表达 1000
在实际的计算机中表达整数时使用了多少灯泡呢?
1983 年,由任天堂推出的家庭计算机中使用了 8 盏灯泡 9,能表达的 整数范围是 0~255。正因如此,有些游戏中计数器到 255 时就会停止,有些游戏在有数值超过 255 时会发生程序错误。
9严格来讲是使用了八位的 CPU。
笔者撰写这本书时是 2013 年。时至今日,PC 机使用 32 盏或 64 盏灯泡已经成为主流 10。32 盏能表达 0~4 294 967 295 之间的数,64 盏能表达 0~18 446 744 073 709 551 615 之间的数。
10内置有 64 位 CPU 的 PC 机在一般的数码城就可以买到了,当然也有很多人还在使用老式 32 位 CPU 的 PC 机。
八进制与十六进制
另外,作为表达数值的方法还有八进制与十六进制。这些又是怎么回事呢?话已至此,我们一起来讲解一下八进制与十六进制。
大家平时使用的是十进制。在十进制中,每个位上使用的是 0 到 9 之间的十个符号。刚刚我们学习的二进制中,每一位上仅仅使用 0 和 1 两个符号。相比十进制,二进制在使用的符号种类更少的同时,表达相同的数字时所需要的字符数量也更多。比如十进制中的 1000 在二进制中表达就是 1111101000,这个也太长了,读起来比较困难。
把二进制中某几个字符组合在一起用一个字符来表示,使之变得更容易读,这种表达方式就是八进制或十六进制。
N进制中使用的字符
二进制 0 1
八进制 0 1 2 3 4 5 6 7
十进制 0 1 2 3 4 5 6 7 8 9
十六进制 0 1 2 3 4 5 6 7 8 9 a b c d e f
八进制
例如把二进制数 1111101000,每三位三位进行切分就变成 001 111 101 000。这样切分后,每一小块有 2×2×2 总计 8 种模式。将这二进制中的三个字符通过以下表中的替换形式各自替换为一个字符后,得到的是 1750。
000 → 0 001 → 1 010 → 2 011 → 3
100 → 4 101 → 5 110 → 6 111 → 7
这里每一位可能使用八种符号,所以被称为八进制。
十六进制
再如,把同样的二进制数,按每四位四位的切分方法,得到的是 0011 1110 1000. 这样切分后,每一小块有 2×2×2 总计 16 种模式。将这二进制中的四个字符通过以下表中的替换形式各自替换为一个字符后,得到的是 3e8。
0000 → 0 0001 → 1 0010 → 2 0011 → 3
0100 → 4 0101 → 5 0110 → 6 0111 → 7
1000 → 8 1001 → 9 1010 → a 1011 → b
1100 → c 1101 → d 1110 → e 1111 → f
这里每一位可能使用十六种符号,所以被称为十六进制。习惯上,通常在八进制表示的数值前加上 0 或 0o,十六进制表示的数值前加 上 0x11。
11以 0 开始的数值,比如,在 C、Ruby、Python 等很多语言中,0100 都被当作是八进制数。但是,也有批评意见认为这样的表达方式容易被误认为是 100. Python 语言从 3.0 版本开始把 0100 作为语法错误,强制要求其表达为 0o100.
Python 3.0
>>> 0o1750
1000
>>> 0x3e8
1000
