模拟与数字
想想台灯上的调光旋钮。你慢慢转动它,灯光就在所有亮度之间平滑滑动——昏暗、稍亮、再亮一点——中间没有任何台阶。这就是模拟信号:它的取值可以是连续范围里的*任意*一个数,就像你说话的音量,或者海面上浪头的高度。
数字信号则故意把这些中间的丰富层次全部丢掉。它不允许无穷多的灰度,只允许两个电平——叫它们高和低,或者1 和 0。它不像调光旋钮,更像一个普通的开关灯:灯泡要么亮、要么灭,中间什么都没有。
为什么要放弃这些细节?因为噪声。导线会沾上杂散的电扰动,就像麦克风会收到背景的「嘶嘶」声一样,所以承载你信号的那个电压总是有点被抹花。对模拟值来说,每一点抹花都会改变结果。可一旦规则只是「足够高就算 1,足够低就算 0」,中间还留着一段宽裕的间隔,那么几个百分点的抹花就完全无所谓了——接收方读到的还是干干净净的 1 或 0。把世界压进两个清清楚楚的选项里,正是数字之所以皮实的原因。
逻辑门
一旦世界只剩 1 和 0,你就需要一些小机器来*拿它们做点事*。这些小机器就是逻辑门,每一个都遵守一条死板而简单的规则:输入一两个比特,输出一个比特。你最先会遇到的三种是 AND(与门)、OR(或门)和 NOT(非门)。
它们的行为和这些英文词的字面意思一样。AND(与)门只在*两个*输入都是 1 时才输出 1——就像一扇得两个人同时转动钥匙才打开的门:0 AND 0 得 0,1 AND 0 得 0,只有 1 AND 1 才得 1。OR(或)门只要*任一*输入是 1 就输出 1,像一盏接在两个开关上的灯——0 OR 0 得 0,而 1 OR 0 就得 1。NOT(非)门是个孤独的唱反调者:喂它 1,它吐出 0;喂它 0,它吐出 1。
那这些门究竟是用什么*做*的呢?主要是晶体管——微小到没有任何活动部件的电开关,每一个都由一根控制线上的电压来拨开或拨关。把几个晶体管按一种方式接起来,它们就服从 AND 规则;换一种接法,你就得到 OR 或 NOT。一块现代芯片里塞着数十亿个这样的开关,于是它就有数十亿个门可供调度。
布尔真值
接下来是把这一切串起来的那个不动声色的妙处。早在计算机出现之前,有位叫乔治·布尔的数学家就琢磨出了真与假的代数——一种像算数字那样去*运算*陈述句的方法。我们今天管它叫布尔代数,它的整个宇宙里只有两个值。如果让真代表 1、假代表 0,那么布尔的真假数学和你那些门的 1/0 行为,根本就是同一件事换了身衣裳。
这一对应,正是从电流通往思考的桥梁。正因为布尔代数是数学里实打实的一个分支,任何人都能拿张纸、一支笔坐下来,*证明*一团乱麻似的门到底会干什么——根本不用动烙铁。而且数学家很久以前就证明了:AND、OR、NOT 这三个不起眼的运算,足以表达你能想象到的任何真假规则,无论它多么繁复。
把足够多的门叠起来,这些简单的选择就开始累加——而且是字面意义上的累加。这就是它的核心:一条把两个单比特相加、并报告结果和是否「进位」的规则。把它当成纯粹的布尔逻辑来读,你会发现整件事不过是 AND、OR、NOT 的伪装。
sum = (A OR B) AND NOT (A AND B) // 1 when exactly one input is 1 carry = A AND B // 1 only when both inputs are 1 // 0 + 0 -> sum 0, carry 0 // 1 + 0 -> sum 1, carry 0 // 0 + 1 -> sum 1, carry 0 // 1 + 1 -> sum 0, carry 1 (i.e. binary 10, "two")
记忆:触发器与时钟
门擅长*反应*,却毫无记忆——你一改输入,输出立刻就忘了它刚才是什么。一台什么都记不住的计算机毫无用处,所以我们需要一种电路,能攥住一个比特,直到被告知改变为止。这种电路就是触发器。
触发器是几个门巧妙地首尾相接、互相回馈构成的一个环路,于是它们把自己的输出撑住、稳稳地保持——你可以想象成一个拨到哪儿就停在哪儿的开关。每个触发器恰好记住一个比特:一个 1 或一个 0。把八个排成一行,你就能存下一个数;把数十亿个排起来,你就拥有了程序赖以生存的内存。
可如果上百万个触发器各自想更新就更新,结果就会一片混乱——到处都是半成品的数字撞在一起。所以芯片里有位指挥:时钟,一个每秒在高低之间翻转成百万乃至上亿次的电压,滴答、滴答。每个触发器都被规定:只在滴答的那一下去看自己的输入并更新。两次滴答之间,大家都纹丝不动,门则悄悄把各自的答案稳定下来。
- 滴答。 每个触发器把此刻输入上的那个比特拍个快照,锁存下来。
- 滴答之间。 这些被攥住的比特流过各个门,门算出下一轮结果——一个和、一次比较、下一条指令。
- 下一次滴答。 触发器捕获这些新鲜的结果,整台机器步调一致地一起向前迈一步。