计算机系统解密:从理解计算机到编写高效代码
上QQ阅读APP看书,第一时间看更新

1.6 表示实数

到目前为止,我们已经设法用二进制来表示整数。但是,如何表示实数呢?实数在十进制中包括一个小数点。我们需要一些方法来表示等价二进制点。同样,这可以通过在不同语境解释二进制位来实现。

1.6.1 定点表示法

用二进制表示分数的一种方法是任意选择一个二进制点(小数点的二进制等价物)位置。比如,如果有4个位,我们可以假设其中两个位在二进制点的右边,表示4个小数,而另外两个位在左边,表示4个整数。这就是所谓的定点表示法,因为二进制点的位置是固定的。表1-7展示了其工作原理。

表1-7 定点二进制数字

从二进制记数法来看,点左边的整数应该看起来很熟悉。与整数表示类似,点的右边两位可以表示4个值,代表的是四分之一,而不是我们熟悉的十进制中的十分之一。

虽然这种方法很好用,但在通用型计算机中并不常用,因为它需要太多的位来表示有用的数字范围。某些被称为数字信号处理器(Digital Signal Processor, DSP)的特殊用途计算机仍然使用定点数。而且第11章将提到定点数在某些应用中是有用的。

通用型计算机是为了解决通用型问题而建立的,涉及的数字范围很广。你可以通过浏览物理学读物,对这个范围有一定的概念。例如,有如普朗克常数(6.63×10–34J·S)的微小数字,也有如阿伏伽德罗常数(6.02×1023mol–1)的大数字。这是一个相差1057的范围,大约是2191。这几乎需要200个比特!用几百个比特去表示这里面的每一个数字成本可真是不低,所以我们需要寻找其他的解决方法。

1.6.2 浮点表示法

我们用二进制版本的科学记数法来解决这个问题,科学记数法常用于表示包括普朗克常数和阿伏伽德罗常数在内的大量级的数字。科学记数法创造了一个新的解释语境,以表示巨大范围内的数字。它使用小数点左边是个位的数字(称为尾数)乘以10的幂(称为指数)来表示。计算机也使用与这类似的表示方法,不过尾数和指数是二进制数,而底数10变成了2。

以上就是浮点表示法,这让人很困惑,因为二进制(十进制)点总是在同一个位置:在一和二分之一(十进制的十分之一)之间。“浮点”只是科学记数法的另一种说法,我们使用浮点把数字表示为1.2×10–3,而不是0.0012。

请注意,我们无须使用任何比特来表示底数2,因为浮点定义默认底数为2。通过将有效数字和指数分开,浮点表示法可以表示非常小或非常大的数字,无须存储所有数字中含有的零。

表1-8显示了4位的浮点表示法,其中2位为尾数,2位为指数。

表1-8 浮点二进制数字

虽然这个例子只使用了几个位,但仍揭示了浮点表示法中存在的一些低效率问题。首先,存在很多浪费的位组合。例如,有四种方式表示0,有两种方式表示1.0、2.0和4.0。其次,并不是每个数字都有表示它的位型,而且当数字变大时,指数会使数字之间的距离更大。这带来的一个副作用是,我们可以把0.5和0.5相加得到1.0,但无法把0.5和6.0相加,因为没有表示6.5的位型。(数学中有一个分支叫作数值分析,涉及对计算的不准确程度的跟踪。)

1.6.3 IEEE浮点标准

奇怪的是,浮点表示法是表示实数的标准方法。浮点表示法使用的位比表1-8中使用的更多,而且有两个符号,一个用于表示尾数,另一个隐藏的符号是指数的一部分。还存在很多技巧,可以确保像四舍五入这样的运算尽可能地成功,并尽量减少浪费的位组合数量。名为IEEE 754的标准将所有这些都列了出来。IEEE(Institute of Electrical and Electronic Engineer)代表美国电气电子工程师协会,它是一个专业组织,其业务包括制作标准。

我们希望在可用的位数下,最大限度地提高精度。有个巧妙的技巧叫作归一化,它可以调整尾数,所以不需要前导(即在左边)的零。每次调整尾数都需要对指数进行相应的调整。第二种技巧来自数字设备公司(Digital Equipment Corporation, DEC),它扔掉了尾数最左边的那一位(因为我们知道最左边那一个位永远是1),将精度提高了一倍,也多了一个位的空间。

你不需要知道IEEE 754标准所有烦琐的细节。但你应该知道经常会遇到的两种类型的浮点数:单精度浮点数和双精度浮点数。单精度数字使用32位表示,可以表示大约在±10±38范围内的数字,可精确到7位数。双精度数字使用64位表示,可以表示的数字范围更广,约在±10±308,可精确到15位数。图1-13显示了它们的排列方式。

图1-13 IEEE浮点数格式

这两种格式都有一个符号位,即图1-13中的S。可以看到,双精度浮点数比单精度浮点数多了3个指数位,范围是单精度的8倍。双精度浮点数也比单精度浮点数多了29个尾数位,精度更高。然而,代价是双精度浮点数要比单精度浮点数多占一倍的位数。

你可能已经注意到,指数值没有明确的符号位。IEEE 754的设计者将所有0和所有1的指数值都设计为有特殊意义,所以实际的指数值必须被压缩到剩余的位型中。他们使用偏置(偏移)指数值来实现这点。对于单精度浮点数,偏移值是127,这意味着127(01111111)的位型代表指数值为0。1(00000001)的位型代表指数值为–126,254(11111110)的位型代表指数值+127。双精度浮点数也类似,只不过它的偏移值为1 023。

IEEE 754的另一个方便之处是,它有特殊的位组合,可以表示除以0之类的东西,它的值为正负无穷大。它还指定了一个叫作NaN的特殊值,代表“不是数字”,所以如果你发现处于NaN状态,可能意味着你做了一些不合理的算术运算。这些特殊的位组合使用了前面讨论过的保留指数值。