浮点数

浮点数在计算机中的表示是基于科学计数法(Scientific Notation)的,我们知道32767这个数用科学计数法可以写成3.2767×104,3.2767称为尾数(Mantissa,或者叫Significand),4称为指数(Exponent)。浮点数在计算机中的表示与此类似,只不过基数(Radix)是2而不是10。下面我们用一个简单的模型来解释浮点数的基本概念。我们的模型由三部分组成:符号位、指数部分(表示2的多少次方)和尾数部分(只表示小数点后的数字)。

 _______________________________
| 1 bit |  5 bits  |   8 bits   |
 -------------------------------
Sign bit  Exponent   Significand

如果要表示17这个数,我们知道17=17.0×100=1.7×101=0.17×102,类似地,17=(10001)2×20=(0.10001)2×25,把尾数的有效数字全部移到小数点后,这样就可以表示为:

 ______________________
| 0 | 00101 | 10001000 |
 ----------------------

如果我们要表示0.25就遇到新的困难了,因为0.25=1×2-2=(0.1)2×2-1,而我们的模型中指数部分没有规定如何表示负数。我们可以在指数部分里规定一个符号位,然而更有效和广泛采用的办法是使用偏移的指数(Biased Exponent)。规定一个偏移值,比如16,实际的指数要加上这个偏移值再填写到指数部分,这样,比16大的就表示正指数,比16小的就表示负指数。要表示0.25,指数部分应该填16-1=15:

 ______________________
| 0 | 01111 | 10000000 |
 ----------------------

现在还有一个问题需要解决:每个浮点数的表示方法都不唯一,例如17=(0.10001)2×25=(0.010001)2×26,这样给计算机处理增添了复杂性。为了解决这个问题,我们规定尾数部分的最高位必须是1,也就是说尾数必须以0.1开头,对指数做相应的调整,这称为正规化(Normalize)。由于尾数部分的最高位必须是1,这个1就不必保存了,可以节省出一位来用于提高精度,我们说最高位的1是隐含的(Implied)。这样17就只有一种表示方法了,指数部分应该是16+5=21=(10101)2,尾数部分去掉最高位的1是0001:

 ______________________
| 0 | 10101 | 00010000 |
 ----------------------

两个浮点数相加,首先把小数点对齐然后相加:

 ______________________
| 0 | 10010 | 10010000 | +
 ----------------------
 ______________________
| 0 | 10000 | 00110110 | =
 ----------------------
 ______________________
| 0 | 10010 | 11011101 | =
 ----------------------

 11.0010000
+ 0.100110110
-------------
 11.101110110

由于计算机浮点数表示的精度有限,计算结果末尾的10两位被舍去了。做浮点运算时要注意精度问题,有时候计算顺序不同也会导致不同的结果,把上面的例子改一下,11.0010000+0.00000001+0.00000001=11.0010000+0.00000001=11.0010000,后面加的两个很小的数全被舍去了,没有起任何作用,但如果换一下计算顺序就能影响到计算结果了:0.00000001+0.00000001+11.0010000=0.00000010+11.0010000=11.0010001。再比如128.25=(10000000.01)2,需要10个有效位,而我们的模型中尾数部分是8位,算上隐含的最高位1一共有9个有效位,那么128.25的浮点数表示只能舍去末尾的1,表示成(10000000.0)2,其实跟128相等了。

浮点数是一个相当复杂的话题,本节只是通过这个简单的模型介绍一些基本概念而不深入讨论,理解了这些基本概念有助于你理解浮点数标准,目前业界广泛采用的符点数标准是由IEEE(Institute of Electrical and Electronics Engineers)制定的IEEE 754