普林斯顿计算机公开课(原书第2版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3 比特、字节和二进制

“世界上只有10种人,理解二进制的和不理解二进制的。”

数字系统用数值来表示所有信息,但令人惊讶的是,这里使用的却不是我们熟悉的以10为基数的十进制,而是二进制,也就是以2为基数的数制。

尽管每个人都或多或少地熟悉算术,但根据我的经验,他们对数字含义的理解有时是不可靠的,至少在以10为基数(完全熟悉)和以2为基数(大多数人都不熟悉)之间进行类比时是这样。我将在本节中尝试解决这个问题,但是如果事情令人困惑,就不断地对自己说:“这就像普通的数字,只不过是逢2进1而不是逢10进1而已。”

2.3.1 比特

表示数字信息的最基本的方法是用比特。正如本章开头的引语所指出的,单词“bit”是二进制数字“binary digit”的缩写,由统计学家约翰·图基(John Tukey)在20世纪40年代中期创造。据说,被称为“氢弹之父”的爱德华·特勒更喜欢“bigit”这个词,可惜这个词并没有流行起来。

单词binary表示有两个值的东西(前缀bi表示2),事实确实如此:比特是一个数字,它的值是0或1,没有其他可能。这可以与十进制数字由0~9的10个可能值进行对比。使用单个比特,我们可以编码或表示涉及从两个值中挑选其中一个的任何选择。这样的二元选择比比皆是:开/关、真/假、是/否、高/低、进/出、上/下、左/右、北/南、东/西等。一个单位就足以确定选择了一对中的哪一个。例如,我们可以将0赋值为off,1赋值为on,反之亦然,只要每个人都同意哪个值代表哪个状态。

图2.7显示了我打印机上的电源开关和在许多设备上看到的标准开关符号。它也是Unicode字符。

图2.7 开关和标准开关符号

单个比特就足以表示开/关、真/假和类似的二元选择,但我们需要一种方法来处理更多选择或表示更复杂的事物。为此,我们使用一组比特,为0和1的不同可能组合赋值。例如,我们可以使用两个比特来表示美国大学的四年:大一(00),大二(01),大三(10)和大四(11)。如果要增加一个类别,例如研究生,那么两个比特是不够的:会出现五个可能的值,但两个比特只有四个不同的组合。三个比特就足够了,然而实际上三个比特可以代表多达八种不同的事物,因此我们还可以算上教师、职员和博士后。组合将是000、001、010、011、100、101、110和111。

有一种模式将比特数与可以用这些数量的比特标记的项数相关联。关系很简单:如果有N个比特,则能表示的不同组合数量为2N,即2×2×…×2(N次),如图2.8所示。

图2.8 2的幂

这类似于十进制数字:如果有N个十进制数字,可以表示10N种不同情况,如图2.9所示。

图2.9 10的幂

2.3.2 2的幂和10的幂

由于计算机中的所有东西都是用二进制来处理的,所以像大小和容量这样的属性往往用2的幂表示。如果有N个比特,就有2N个可能的值,所以知道2的某个值的幂是很方便的,比如210

一旦数字变大,它们当然不值得记住。幸运的是,有一个可以很好地近似的捷径:某些2的幂次值接近于10的幂次值,有一种易于记忆的规则,如图2.10所示。图2.10增加了一个描述大小的前缀peta或1015,它的发音像“pet”,而不是“Pete”。本书最后的词汇表中包含了一个更大的表,里面有更多的单位。

图2.10 2的幂和10的幂

随着数值的增大,近似值变得更不准确,但在1015时,它也仅高出12.6%,所以在很大范围内还是可用的。你会发现人们经常模糊2的幂和10的幂之间的区别(有时是朝着有利于他们试图达到的某个目标的方向),所以1K可能用于表示1000,也可能表示210或1024。这通常是一个很小的区别,所以采用2和10的幂是对包含比特的大数进行心算的好方法。

2.3.3 二进制数值

如果按照通常的进位法则来解释,那么一系列比特就可以表示一个数值,只不过此时的基数是2,而不是10。0~9的10位数字,足以给最多10个条目分配标签。如果数量超过10,则必须使用更多位数字,比如两位十进制数字可以表示的数值或标签能达到100个,即00~99。多于100项的时候,就要用三位数字,其表示的范围是1000,即000~999。对于普通的数字,我们通常不会写出数值前导的零,但这些零是隐含的。另外,我们平时计数也是从1而非0开始的。

十进位数是10的幂的和的简写;例如,1867是1×103+8×102+6×101+7×100,也就是1×1000+8×100+6×10+7×1,即1000+800+60+7。在小学里,你可能称这些为个位数,十位数,百位数等。这些我们是如此熟悉,以至于我们很少去想它。

二进制数也是一样的,除了基数是2而不是10,以及所涉及的数字只有0和1。像11101这样的二进制数被解释为1×24+1×23+1×22+0×21+1×20,我们以10为基数表示为16+8+4+0+1,或29。

比特序列可以被解释为数值,这意味着有一种自然的模式给条目分配二进制标签:将它们按数值顺序排列。前面我们看到了为大一、大二、大三、大四学生分配的标签00、01、10、11,它们分别是十进制数值的0、1、2、3。接下来的序列是000、001、010、011、100、101、110、111,也就是十进制数值0~7。

下面我们做个练习,看你理解了多少。我们都很熟悉用手指数到10,但是如果你用手指(每个手指为一个数位!)代表一个二进制数,最多能数到多少?值的范围有多大?如果你数到6时发现它的二进制表示是一个似曾相识的手势,那说明我前面讲的你都理解了。

前面我们都看到了,把二进制转换成十进制很容易:只要把相应位置上值为1的2的对应次幂加起来即可。而把十进制转换成二进制要难一些,但也不会难太多。反复地用2除十进制数。每次除完,把余数写下来,余数要么是0,要么是1,然后再用2除商。这样反复除下去,直到原来的数被除到等于0。最后得到的余数的序列,就是相应的二进制数,但顺序相反,所以最后要将其倒转。

举例说明,图2.11表示了将十进制数1 867转换成二进制数的过程。反向读取这些位,能得到111 0100 1011,把相应位上为1的2的对应次幂加起来可以验算:1024+512+256+64+8+2+1=1867。

图2.11 将十进制数1 867转换为二进制数111 0100 1011

整个过程的每一步都会产生剩余数值的最低有效位(即最右边的位)。其实,把一个很大秒数表示的时间转换成日、时、分、秒的过程与此类似:除以60得到分钟(余数是秒),结果除以60得到小时(余数是分钟),结果再除以24得到天数(余数是小时)。区别在于时间转换使用了不止一个基数,而是混合使用了60和24。

你也可以按照降幂的顺序通过从原来的数字中逐个减去2的幂来将十进制转换为二进制。从小于此数的2的最高次幂开始,比如从1867中减去210。每减去2的一个幂,就写1,如果2的幂大于剩下的值,就写0,就像在上面这个例子中27或者128的情况。最终得到的由“1”和“0”组成的序列就是二进制值。这种方法可能更直观,但不那么机械化。

二进制算术很简单,因为总共才两个数字,加法和乘法表都只有两行两列,如图2.12所示。虽然你将来不太可能自己动手做二进制算术,但这两个表的简单性其实也说明了为什么相对于十进制算术,执行二进制计算的计算机电路要简单得多。

图2.12 二进制加法和乘法表

2.3.4 字节

在所有现代计算机中,处理和存储组织的基本单位是作为一个单位来处理的8个比特。一组8个比特被称为1个字节,这个词是由IBM的计算机架构师沃纳·布赫霍尔兹在1956年创造的。1个字节可以编码256个不同的值(28,8个0和1的所有不同组合),可以是0到255之间的整数,或7位ASCII字符集中的一个字符(有1个多余的位),或其他东西。通常,1个特定的字节是更大的字节组的一部分,这个字节组代表了一个更大或更复杂的事物。2个字节加起来提供16比特,足以表示0~216-1(或65535)之间的一个数字。这也可以表述Unicode字符表中的任意字符,也许是

这两个字符中的其中一个;每个字符占2个字节。4个字节是32比特,这可能代表4个ASCII字符,或者2个Unicode字符,或者不大于232-1(约43亿)的一个整数。一组字节能表示的内容不受限制,然而处理器本身定义了适当的特定分组,例如大小不同的整数,并且有处理这些分组的指令。

如果我们想记下一个或多个字节所代表的数值,我们可以用十进制形式表示。它本身就是数字,这对于人类读者来说很方便。我们可以把它写成二进制来查看单个的位,尤其是如果不同的位编码不同种类的信息,这就很重要。但是,二进制很笨重,比十进制长三倍多,所以通常使用一种称为十六进制的替代记数法。十六进制以16为基数,所以它有16个数字(就像十进制有10个数位,二进制有2个数位),这些数字是0、1、…、9、A、B、C、D、E和F。每个十六进制数字代表4个位,图2.13用十六进制表示这些数值。

除非你是程序员,否则你只会在少数地方看到十六进制。其中之一是网页颜色。之前提过,计算机中色彩的最通常的表示方式是使用3个字节来表示一个像素,分别代表红、绿和蓝的量,这称为RGB编码。每个组成都存储在1个字节中,所以红色、绿色和蓝色的值各有256种可能,总共就是256×256×256种颜色,听上去很多。我们可以用2和10的幂来快速估计下到底有多少种。也就是28×28×28,即224,或是22×220,或是16×106,或者说1600万。你大概看到过用来描述计算机显示器的数字:“超过1600万种颜色!”这个估算其实比实际低了百分之五,224的实际数值为16777216。

图2.13 十六进制数位及对应二进制数值表

强烈的红色像素将被表示为FF0000,即红色为十进制最大数255,没有绿色,没有蓝色。而明亮但不强烈的蓝色,就像许多网页上的链接的颜色,将是0000CC。黄色是红色加绿色,所以FFFF00可得到最亮的黄色,灰色的阴影有着等量的红色、绿色和蓝色,所以中等灰色像素为808080,也就是等量的红绿蓝。黑和白则分别是000000和FFFFFF。

十六进制值也用于Unicode编码表以标识字符:

的十六进制编码是67714EAC。第8章将会介绍用十六进制数表示的以太网地址,第10章会讨论用十六进制数表示URL中的特殊字符。

有时候你会在计算机广告中看到“64位”这个词(“Windows10家庭版64位”),这是什么意思呢?计算机内部是以不同大小的块处理数据的,这些块包含数值,这些数值用32位和64位表示比较方便。地址也是如此,也就是主存中信息的存储位置。前面提到的64位即指地址这个属性。30年前地址从16位升级到了32位,这样就足够访问4GB的内存了。现在,通用计算机从32位到64位的过渡也几乎完成了。我不会试图预测从64位到128位的过渡何时会发生,但64位应该会持续一段时间。

在所有这些关于比特和字节的讨论中,要记住的关键是一组比特的意义取决于它们的上下文,单独看它们自身数据是不知道其含义的。1个字节可以只用1个比特来表示真或假,另外7个空闲不用,也可以用来保存一个较小的整数,或者一个#之类的ASCII字符。它还可能是另一个书写系统中的一个字符的一部分,也可能是一个2字节、4字节或8字节的更大数字的一部分,或者是一幅画或一段音乐的一部分,甚至是供CPU执行的一条指令的一部分,还有很多其他的可能(这和十进制数的情况是一样的。根据具体情况,一个3比特十进制数可以代表美国地区代码、高速公路号码、棒球击球率或许多其他东西)。

一个程序的指令有时是另一个程序的数据。当你下载程序或手机应用时,它只是数据:一些被盲目复制的比特。但是当你运行这些程序时,处理器在处理时它们的比特就被当作指令处理。