IEEE 754标准

在C/C++等编程语言中,浮点数在计算机中的表示采用的是IEEE 754标准。该标准一共的规定了四种表示浮点数值的方式,其中常见的表示方式有两种:单精确度(32位)和双精确度(64位)。这两种表示方式在C/C++语言中分别可以用float和double关键字来定义。

单精度浮点数的表示方法

单精度浮点数的表示方法其实就是把一个32位分成了三个部分:

  1. 第一个部分,占1位,表示浮点数的符号,英文Sign,简称为S。
  2. 第二个部分,占8位,表示浮点数的指数,英文Exponent,简称为E。
  3. 第三个部分,占23位,表示浮点数的尾数,英文Mantissa,简称为M。

如下图所示:

 31         22                    0
+----------------------------------+
|S|EEEEEEEE|MMMMMMMMMMMMMMMMMMMMMMM|
+----------------------------------+
   30     23

对应浮点数表示的值为:

(−1)S⋅(1+M223)⋅2(E−127)(-1)^S \cdot (1 + \frac{M}{2^{23}}) \cdot 2^{(E-127)}

结合上面的公式,来理解各个部分的意义就更容易了:

  1. 符号部分,(−1)S(-1)^S

    当S为0时,(−1)0=1(-1)^0 = 1,表示浮点数的值大于0;同理,当S为1时,(−1)1=−1(-1)^1 = -1,表示浮点数的值小于0。

  2. 指数部分,2(E−127)2^{(E-127)}

    首先要明白:对于任何一个正实数x,都可以找到一个整数n,使得2n<=x<2n+12^n <= x < 2^{n+1}。举个例子,比如5,可以找到n=2,使得22<=5<232^2 <= 5 < 2^3,即4<=5<84 <= 5 < 8。浮点数中的指数部分就是这里的n。

    那么为什么还要减去127呢?因为当浮点数值的绝对值小于1时,指数部分其实是小于0的。举个例子,比如0.45,只有n=−2n=-2时,使得2−2<=0.45<2−12^{-2} <= 0.45 < 2^{-1},即0.25<=0.45<0.50.25 <= 0.45 < 0.5。而浮点数中指数部分占8位,可以表示范围[0-282^8),即[0-256),为了在指数部分能够表示负数,IEEE 754规定减去127,即n=E−127n = E - 127。所以,如果浮点数的n=−2n=-2,那么实际的E应该是125。

  3. 尾数部分,(1+M223)(1 + \frac{M}{2^{23}})

    尾数部分占23位,可以表示范围[0-2232^{23}),即[0-8388608)。

    可以这样理解尾数部分:把一条线分成8388608个段,也就是把2n2^n到2n+12^{n+1}分成8388608个线段。尾数部分的值M,表示从2n2^n到浮点数值的绝对值x所要经过的线段数量,也就是2n2^n到x的长度占2n2^n到2n+12^{n+1}长度的比例是多少。这个比例的值就是尾数部分公式中M223\frac{M}{2^{23}}的来由,而加1则表示线段的起点2n2^n的值。

单精度浮点数举例

有了上面的说明,再结合例子来理解一下,比如浮点数3.14:

  1. 符号部分

    因为3.14大于0,所以S=0S = 0。

  2. 指数部分

    21<=3.14<222^1 <= 3.14 < 2^2,所以n=1n=1,那么E=n+127=128E = n + 127 = 128。

  3. 尾数部分

    212^1到3.14的长度占212^1到222^2长度的比例是(3.14−21)(22−21)=0.57\frac{(3.14 - 2^1)}{(2^2 - 2^1)} = 0.57

总线段数量为2232^{23},那么,从212^1到3.14所要经过的线段数量M=0.57∗223=4781506.56M = 0.57 * 2^{23} = 4781506.56,四舍五入,转换成整数结果为M=4781507M = 4781507。因为有四舍五入,所以浮点数保存的数据和实际数据是有误差的。

把S、E、M转换成二进制,就可以得到3.14的二进制表示0x4048f5c3:

 S     E             M
+----------------------------------+
|0|10000000|10010001111010111000011|
+----------------------------------+

最后代入浮点数公式计算:

(−1)0⋅(1+4781507223)⋅2(128−127)=3.1400001049041748046875(-1)^0 \cdot (1 + \frac{4781507}{2^{23}}) \cdot 2^{(128-127)} = 3.1400001049041748046875

也可以使用IEEE-754浮点数转换工具来分析浮点数的表示方法。

双精度浮点数的表示方法

双精度浮点数和单精度浮点数的原理是一样的,只是各个部分长度不同而已。

双精度浮点数的表示方法其实就是把一个64位分成了三个部分:

  1. 第一个部分,占1位,表示浮点数的符号,英文Sign,简称为S。
  2. 第二个部分,占11位,表示浮点数的指数,英文Exponent,简称为E。
  3. 第三个部分,占52位,表示浮点数的尾数,英文Mantissa,简称为M。

对应浮点数表示的值为:

(−1)S⋅(1+M252)⋅2(E−1023)(-1)^S \cdot (1 + \frac{M}{2^{52}}) \cdot 2^{(E-1023)}