2.4 浮点数
浮点表示对形如 的有理数进行编码。他对执行涉及非常大的数字、非常接近于0的数字 ,以及更普遍的作为实数运算的近似值的计算,是很有用的。
直到20世纪80年代,每个计算机制造商都设计了自己的表示浮点数的规则,以及对浮点数执行运算的细节。另外,他们常常不会太多的关注运算的精确性,而把实现的速度和简便性看的比数字的精确性更重要。
大约在1985年,这些情况随着IEEE标准754的推出而改变了,这是一个仔细制定的表示浮点数及其运算的标准。这项工作从1976年开始有Intel赞助的,与8087的设计同时进行,8087是一种为8086处理器提供浮点支持的芯片,他们请William Kahan作为顾问,帮助设计未来处理器浮点标准。目前,实际上所有的计算机都支持这个后来被称为IEEE浮点的标准。着大大的提高了科学应用程序在不同机器上的可移植性。
在本节中,我们将看到IEEE浮点格式中数字是如何表示的。我们还将探讨舍入的问题。即当一个数字不能被准确的表示为这种格式时,就必须向上调整或者向下调整。然后我们将探讨加法、乘法和关系运算符的数学属性。
2.4.1 二进制小数
理解浮点数的第一步是考虑含有小数值的二进制数字。首先,让我们来看看更熟悉的十进制表示法。十进制表示法使用如下形式的表示:
其中每个十进制数的取值范围是0~9。这个表达描述的数值d定义如下:
数字权的定义与十进制小数点符号相关,这意味着小数点左边的数字的权是10的正幂,得到整数值,而小数点右边的数字的权是10的负幂,得到小数值。
假定我们仅考虑有限长度的编码,那么十进制表示法不能准确的表达像 1/3和 5/7这样的数,类似,小数的二进制表示法只能表示那些能够被写成 的数。其他的值只能近似的表示。例如,数字1/5可以用十进制小数0.20精确表示。不过我们并不能把它们准确的表示为一个二进制小数,我们只能近似的表示他,增加二进制表示的长度可以提高表示的精度。
2.4.2 IEEE浮点表示
前一节中谈到的定点表示法不能很有效的表示非常大的数字。例如,表达式是用100后面跟随100个零的位模式来表示的。相反,我们希望通过给定x和y的值,来表示 的数。
IEEE浮点标准用的形式来表示一个数:
- 符号(sign) s决定这个数是负数(s=1) 还是正数(s=0),而对于数值0的符号位解释作为特殊情况处理。
- 尾数(significand) M是一个二进制小数。
- 阶码(exponent) E的作用是对浮点数加权,这个权重是2的E次幂。
将浮点数的位表示划分为三个字段,分别对这些值进行编码:
- 一个单独的符号位s 直接编码符号s。
- k位的阶码字段 编码阶码E。
- n位小数字段 编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0
图2-32中给出了将这3个字段装进字中最常见的格式。在单精度浮点格式中,s、exp和frac 字段分别为1位,k=8位和n=23位,得到一个32位的表示。在双精度中,s、exp和frac 字段分别为1位、k=11位和n=52位,得到一个64位表示
给定位表示,根据exp的值,被编码的值可以分成三种不同的情况。图2-33 说明了对单精度格式的情况
情况1:规格化的值:
这种情况是最普遍的。当exp 的位模式既不全为0,也不全为1时,都属于这类情况。在这种情况中,阶码字段被解释为偏置形式表示的有符号整数。也就是说,阶码的值是 ,其中e是无符号数,其位表示为 而Bias是一个等于的偏置值。由此产生指数的取值范围。
小数字段frac被解释为描述小数值,其中,其二进制表示为,也就是二进制小数在最高有效位的左边。尾数定义为 。有时,这种方式也叫做隐含的以1开头的表示,因为我们可以吧M看成一个二进制表达式为 。既然我们总是能够调整阶码E,使得尾数M在范围 之中,那么这种表示方法是一种轻松获得一个额外精度位的技巧。既然第一位总是等于1,那么我们就不需要显式的表示他。
情况2:非规格化的值
当阶码域全0时,所表示的数是非规格化形式。在这种情况下,阶码值是,而尾数,也就是小数字段的值,不包含隐含的开头的1。
非规格化数有两个用途。首先,他们提供了一种表示数值0的方法,因为使用规格数,我们必须总是是M≥1,因此我们不能表示0。实际上,+0.0的浮点表示位模式为全0,符号位是0,阶码字段全为0,而小数域也全是0,这就得到了M=f=0。令人奇怪的是,当符号位为1,而其他域全为0时,我们得到值-0.0.根据IEEE浮点格式,值+0.0和-0.0在某些方面被认为是不同的,而在其他方面是相同的。
非规格化数的另外一个功能就是表示哪些非常接近于0.0的数。他们提供了一种属性,称为逐渐下溢,其中,可能的数值分布均匀的接近于0.0。
情况3:特殊值
最后一类数值是当值阶码全为1时的时候出现的。当小数域全为0时,得到的值表示无穷,当s=0时是+∞,或者当s=1时是-∞。当我们把两个非常大的数相乘,或者除以零时,无穷能够表示溢出的结果。当小数域为非零时,结果值被称为“NaN”,即“不是一个数”的缩写。一些运算的结果不能是实数或无穷,就会返回这样的“NaN”值,比如当计算 或 ∞ - ∞ 时,在某些应用中,表示未初始化的数据时,他们也很有用处。
2.4.3 数字示例
图 2-34 展示了一组数值,他们可以用假定的6位格式来表示,有k=3的阶码位和n=2的位数。偏置量是 。图中的a部分显示了所有可表示的值。两个无穷值在两个末端。最大数值的规格化数是±14。非规格化数聚集在0的附近。图b部分中,我们只展示了介于-1.0和+1.0之间的数值,这样就能够看得更加清楚了。两个零是特殊的非规格化数。可以观察到,那些可表示的数并不是均匀分布的——越靠近原点处他们越稠密。
图2-35 展示了假定的8位浮点格式的示例,其中k=4的阶码位和n=3的小数位。偏置量为。图被分成三个区域,来描述三类数字。不同的列给出了阶码字段是如何编码阶码E的,小数字段是如何编码为数M的,以及他们一起是如何形成要表示的值的。从0自身开始,最靠近0的是非规格化数。这种格式的非规格化数的E=1-7 = -6,得到权 。小数f的值的范围是 ,从而得到范围
描述 | 位表示 | 指数 | 小数 | 值 | |||||
V | 十进制 | ||||||||
0 | 0 0000 000 | 0 | -6 | 0 | 0.0 | ||||
最小的非规格化数 | 0 0000 001 | 0 | -6 | 0.001953 | |||||
0 0000 010 | 0 | -6 | 0.003906 | ||||||
0 0000 011 | 0 | -6 | 0.005859 | ||||||
...... | |||||||||
最大的非规格化数 | 0 0000 111 | 0 | -6 | 0.013672 | |||||
最小的规格化数 | 0 0001 000 | 1 | -6 | 0.015625 | |||||
0 0001 001 | 1 | -6 | 0.017578 | ||||||
...... | |||||||||
1 | 0 0110 110 | 6 | -1 | 0.875 | |||||
0 0110 111 | 6 | -1 | 0.9375 | ||||||
0 0111 000 | 7 | 0 | 1 | 1 | 1.0 | ||||
0 0111 001 | 7 | 0 | 1 | 1.125 | |||||
0 0111 010 | 7 | 0 | 1 | 1.25 | |||||
...... | |||||||||
0 1110 110 | 14 | 7 | 128 | 224 | 224.0 | ||||
最大的规格化数 | 0 1110 111 | 14 | 7 | 128 | 240 | 240.0 | |||
无穷大 | 0 1111 000 | - | - | - | - | - | - | ∞ | - |
图2-36展示了一些重要的单精度和双精度浮点数表示和数字值。根据图2-35中展示的8位格式,我们能够看出有k位阶码和n位小数的浮点数表示的一般属性。
描述 | exp | frac | 单精度 | 双精度 | ||
值 | 十进制 | 值 | 十进制 | |||
0 | 00......00 | 0......00 | 0 | 0.0 | 0 | 0.0 |
最小非规格化数 | 00......00 | 0......01 | ||||
最大非规格化数 | 00......00 | 1......11 | ||||
最小规格化数 | 00......01 | 0......00 | ||||
1 | 01......11 | 0......00 | 1.0 | 1.0 | ||
最大规格化数 | 11......10 | 1......11 |
- 值+0.0总有一个全为0的位表示。
- 最小的正规格化值的表示,是由最低有效位为1而其他所有位为0构成的。它具有小数(和尾数)值 M = f = 和阶码值 E = 。因此它的数字值是 。
- 最大的非规格化值的位模式是由全为0的阶码字段和全为1的小数字段组成的。他有小数(和尾数)值M = f = (我们写成)和阶码值 E = 。因此数值 ,这仅比最小的规格化值小一点。
- 最小的正规格化值的位模式的阶码字段的最低有效位为1,其他位全为0。他的尾数值M=1,而阶码值 。因此,数值。
- 值1.0的位表示的阶码字段除了最高有效位等于0外,所有其他位都等于1。他的尾数值是 M=1,而他的阶码值是E=0。
- 最大的规格化值的位表示的符号位时0,阶码的最低有效位等于0,其他位等于1。他的小数值 ,尾数。他的阶码值,得到数值
2.4.4 舍入
因为表示方法限制了浮点数的范围和精度,所以浮点运算只能近似的表示实数运算。因此,对于值x,我们一般想用一种系统的方法,能够找到“最接近”比配置x,他可以用期望的浮点形式表示出来。这就是舍入运算的任务。一个关键问题是在于两个可能值的中间确定舍入方向。例如,如果我有1.50美元,想把它舍入到最接近的美元数,应该是1美元还是2美元呢?一种可选择的方法是未出实际的数字的上界和下界。例如,我们可以确定可表示的值、,使得x的值位于他们之间。IEEE浮点格式定义了四种不同的舍入方式。默认的方法是找到最接近的匹配,而其他三种可用于计算上界和下界。
图2-37举例说明了四种舍入方式,讲一个金额数舍入到最接近的整数美元数。向偶数舍入,也被称为向最近的值舍入,是默认的方式,试图找到一个最接近的匹配值。因此,它将1.40美元舍入成1美元,而将1.6美元舍入成2美元,因为他们是最接近的整数美元值。唯一的设计决策是确定两个可能结果中间数值的舍入效果。向偶数舍入方式采用的方法是:它将数字向上或者向下舍入,是的结果的最低有效数字是偶数。因此,这种方法将1.5美元和2.5美元都舍入成2美元。
方式 | 1.40 | 1.60 | 1.50 | 2.50 | -1.50 |
向偶数舍入 | 1 | 2 | 2 | 2 | -2 |
向零舍入 | 1 | 1 | 1 | 2 | -1 |
向下舍入 | 1 | 1 | 1 | 2 | -2 |
向上舍入 | 2 | 2 | 2 | 3 | -1 |
其他三种凡是产生的实际值的确界。这些方法在一些数字应用中是很有用的。向零舍入方式是把正数向下舍入,把负数向上舍入。