《计算机科学概论(第12版)》—第1章1.7节小数的存储

本节书摘来自异步社区《计算机科学概论(第12版)》一书中的第1章1.7节小数的存储,作者【美】J. 格伦•布鲁克希尔(J. Glenn *shear) , 丹尼斯•布里罗(Dennis Brylow),更多章节内容可以访问云栖社区“异步社区”公众号查看。

*1.7 小数的存储
不同于整数存储,对于包括小数部分的数值的存储,我们不仅要存储代表其二进制表示的0和1,还要存储其小数点的位置。有一种流行的基于科学记数法的存储方法,称为浮点(floating-point)记数法。

1.7.1 浮点记数法
为了解释浮点记数法,我们来看一个只用一个字节来存储的例子。尽管机器通常使用更长的模式,但是这种8位格式也可以表示实际的系统,而且既可以展示重要的概念,又避免了长位模式的混乱。

首先,我们规定这个字节的高位端为符号位。再次说明,符号位中的二进制0代表存储的数值为非负值,1代表数值为负值。接着,我们将这个字节剩余的7个位分为2组,或称其为域:指数域(exponent field)和尾数域(mantissa field)。我们规定符号位右边的3个位为指数域,余下的4个位为尾数域。图1-24描述了如何拆分字节。


《计算机科学概论(第12版)》—第1章1.7节小数的存储

我们可以借助下面的例子解释这些域的含义。假如一个字节由位模式01101011组成。利用前面的形式分析这个模式,可以看出符号位是0,指数是110,尾数是1011。为了对这个字节解码,我们首先要求解它的尾数,并在它的左边放置一个小数点,得到

.1011

接着,我们求解指数域(110)的内容,并将其解释为一个用3位余码方法(见图1-23)存储的整数。从而得出,我们所举例子的指数域模式表示正数2。这就要求我们将上面所得结果的小数点向右移动2位。(负指数域就意味着向左移动小数点。)因此,我们可以得到

10.11

这就是2frac{3}{4}的二进制表示(二进制中的小数的表示参见图1-18)。接着,我们看到例子中的符号位是0,因此所表示的值为非负值。最后得出结论:字节01101011表示2frac{3}{4}。如果模式是11101011(除了符号位都与之前相同),表示的数值就将为-2frac{3}{4}。

再看一个例子,字节00111100。求尾数后得到

.1100

因为指数域(011)表示数值-1,所以将小数点向左移动一位,得到

.01100

这表示frac{3}{8}。因为原始模式中的符号位是0,所以存储的数值为非负值。最后得出结论:模式00111100表示frac{3}{8}。

在用浮点记数法存储数值时,要颠倒前面的过程。例如,为了对1frac{1}{8}编码,我们首先要将其用二进制记数法表示,得到1.001。接着,我们要从左到右从二进制表示的最左边的1开始,将其位模式复制到尾数域。此时,这个字节如下:

        1 0 0 1

我们现在必须填充指数域。为了达到这个目的,假定尾数域的左边有一个小数点,然后规定位的数量以及小数点移动的方向,以此得到原始的二进制数字。在这个例子中我们可以看到,.1001中的小数点要向右移动一位才能得到1.001,指数因此为正1,所以我们将101(在余4记数法中表示正1,见图1-23)置于指数域。最后,因为存储的数值是非负的,我们用0填充符号位。完成的字节如下:

0 1 0 1 1 0 0 1

当填充尾数域时,你可能会漏掉一个微妙的细节。这个规则是从最左边的1开始,从左到右复制以二进制表示的位模式。为阐述清楚,让我们考虑一下存储数值frac{3}{8}的过程,它的二进制记数法表示为.011。这时,其尾数为

        1 1 0 0

而不会是

        0 1 1 0

这是因为我们是从二进制表示最左边的1开始填充尾数域。遵循这个规则的表示称为规范化形式(normalized form)。

使用规范化形式减少了同一数值多种表示的可能性。例如,00111100和01000110都可以解码成frac{3}{8},但是只有第一个模式才是规范化形式。遵循规范化形式也意味着,所有非0数值的表示都会有一个以1开始的尾数。不过,数值0是一个特例,它的浮点表示就是全部为0的位模式。

1.7.2 截断误差
下面我们来考虑一下,用我们的1字节浮点记数系统存储数值2frac{5}{8},看看会出现什么恼人的问题。我们首先用二进制写2frac{5}{8},得到10.101。但是,当把这个模式复制到尾数域时,我们就用尽了空间,最右边的1(表示最后的frac{1}{8})因此丢失了(见图1-25)。如果现在忽视这个问题,继续填充指数域和符号位,那么我们最后得到的位模式将为01101010,它表示的是2frac{1}{2},而不是2frac{5}{8}。这个现象称为截断误差(truncation error)或舍入误差(round-off error)。这就意味着,由于尾数域空间不够大,存储的部分数值丢失了。


《计算机科学概论(第12版)》—第1章1.7节小数的存储

使用较长的尾数域可以减少这种误差的发生。事实上,现在生产的大多数计算机都至少采用32位存储浮点记数法表示的数值,而不是我们在本书中采用的8位。这同时使得指数域也更长。不过,即使有这样较长的格式,有时候还是需要更高的精确度。

截断误差的另外一个来源是十进制记数法中比较常见的一个现象,即无穷展开式问题,例如,我们在用十进制形式表示frac{1}{3}的时候。有些数值无论我们用多少位数字都无法精确地表示。传统的十进制记数法与二进制记数法的区别在于,二进制记数法中有无穷展开式的数值多于十进制。例如,数值frac{1}{10}表示为二进制时为无穷展开式。想象一下,一个粗心的人用浮点记数法存储和处理美元与美分时会产生什么样的问题?尤其是,如果美元被用作度量单位,那么一角就不能被精确地存储。其中一个解决方式就是,以分为单位处理数据,这样所有的数值就都是整数,都可以用诸如二进制补码这样的方法精确存储。

单精度浮点数
 

1.7节介绍的浮点记数法过于简单,不能用于实际的计算机中。毕竟,在全部实数中,这一记数法的8位只能表示其中256个数。我们在讨论中使用了8位模式来保持示例的简单性,但依然涵盖了重要的基本概念。

现在的许多计算机都支持32位形式的单精度浮点(single precision floating point)记数法。这一格式使用1位表示符号位,用8位表示指数(余码记数法中的),用23位表示尾数。因此,单精度浮点最多有7位十进制有效数字,可以表示极大的数(数量级为1038)直至极小的数(数量级为10-37)。也就是说,给定一个十进制数,可以非常精确地存储前7位十进制有效数字(但仍有可能存在少量误差),前7位之后的数字一定会因截断误差丢失(虽然数字的近似值会被保留下来)。

另一种形式是64位的双精度浮点(double precision floating point)记数法,最多有15位十进制有效数字。
截断误差和与之相关的问题是工作在数值分析领域的人们每天都很关注的问题。这个数学分支研究的是执行大规模、高精度有效计算所涉及的问题。

下面的例子可以激起任何一位数值分析家的兴趣。假设我们要应用前面定义的1字节浮点记数法来做这3个数值的加法:

2frac{1}{2}+frac{1}{8}+frac{1}

如果我们按照上述顺序加数值,首先就是$2\frac{1}{2}$加上$\frac{1}{8}$,得到$2\frac{5}{8}$,二进制表示为10.101。遗憾的是,因为这个数值不能被精确地存储(如同前面所看到的),我们第一步的结果最后被存储为$2\frac{1}{2}$(与其中一个加数相同)。下一步是把这个结果再加到最后的$\frac{1}{8}$上。截断误差在这里再一次出现,最后的结果是错误的$2\frac{1}{2}$。

现在让我们以相反的顺序来加这些数值:首先将$\frac{1}{8}$加到$\frac{1}{8}$上,得到$\frac{1}{4}$,其二进制表示为.01。于是,第一步的结果在一个字节里被存储为00111000,这是精确的。然后,我们将这个$\frac{1}{4}$加到数列中的下一个数值$2\frac{1}{2}$上,得到$2\frac{3}{4}$,我们可以在一个字节里精确地将其存储为01101011。这次的答案是正确的。

总而言之,在浮点记数法表示的数字值加法中,它们相加的顺序很重要。问题是,如果一个很大的数字加上一个很小的数字,那么小数字就可能被截断。因此,多个数值相加的一般规则是先加小数字,即希望在将它们加到一个较大的数值上时,能累计成一个显著的值。这就是前面例子中反映的现象。

现在商用软件包的设计师们在这方面做得很好,他们使没有经过培训的使用者也能很好地避免这种问题的发生。在一个典型的电子表格系统中,除非相加的各个数值大小差别达到1016或更多,否则所得结果都是正确的。因此,如果你认为有必要对数值

10 000 000 000 000 000

加1,那么会得到答案

10 000 000 000 000 000

而不是

10 000 000 000 000 001

这样的问题在一些应用(如导航系统)中是很严重的,微小的误差会在加法运算中累加,最终产生严重的后果。但是,对于一般的PC使用者,大多数商用软件提供的精确度已经足够了。

问题与练习

1.用文中所述的浮点格式对下列位模式进行解码。

  a. 01001010   b. 01101101  c. 00111001   d. 11011100     e. 10101011

2.将下列数值编码成文中所述的浮点格式。指出截断误差的出现情况。

  a. 2frac{3}        b. 5frac{1}     c. frac{3}    d.-3frac{1}       e. -4frac{3}

3.根据文中所述的浮点格式,模式01001001和00111101中哪一个表示的值更大?描述一种确定哪个模式表示的值更大的简单过程。

4.使用文中所述的浮点格式时,可以表示的最大值是什么?可以表示的最小正值是什么?

上一篇:24、深入理解计算机系统笔记,虚拟存储器,动态存储器分配


下一篇:Flink SQL 实战:HBase 的结合应用