简介

浮点数是我们在程序里常用的数据类型,它在内存中到底是怎么样的形式存在的呢?

现代计算机中,一般都以IEEE 754标准存储浮点数,IEEE标准用: V=(-1)^s x M x 2^E 来表示一个浮点数

  • 符号: s决定这个数是正数还是负数
  • 尾数: M是一个二进制小数
  • 阶码: E是对浮点数进行加权,权重是2的E次幂
    符号 阶码 尾数
    sign exponent fraction

对于不同长度的浮点数,阶码与小数位分配的数量不一样,如下:

精度 数符 阶码 尾数 总位数 偏移值
单精度(C中的float) 1 8 23 32 127
双精度(C中的double) 1 11 52 64 1023

对于32位的单精度浮点数,数符分配是1位,阶码分配了8位,尾数分配了是23位。

根据这个标准,我们来尝试把一个十进制的浮点数转换为IEEE 754标准表示。

规格化的值

最普遍的情况,当exp的位模式不全为0(数值0),也不全为1(单精度255,双精度2047)
例如:178.125

  1. 先把浮点数分别把整数部分和小数部分转换成2进制

    1. 整数部分用除2取余的方法,求得:10110010

    2. 小数部分用乘2取整的方法,求得:001

    3. 合起来即是:10110010.001

    4. 转换成二进制的浮点数,即把小数点移动到整数位只有1,即为:1.0110010001 * 2^111,111是二进制,由于左移了7位,所以是111

  2. 把浮点数转换二进制后,这里基本已经可以得出对应3部分的值了

    1. 数符:由于浮点数是正数,故为0.(负数为1)

    2. 阶码 : 阶码的计算公式:阶数 + 偏移量, 阶码是需要作移码运算,在转换出来的二进制数里,阶数是111(十进制为7),对于单精度的浮点数,偏移值为01111111(127)[偏移量的计算是:2^(e-1)-1, e为阶码的位数,即为8,因此偏移值是127],即:111+01111111 = 10000110

    3. 尾数:小数点后面的数,即0110010001

    4. 最终根据位置填到对位的位置上:

      | 数符 | 阶码 | 尾数 |
      |:——:|:——:|:——:|
      |0|1 0 0 0 0 1 1 0|0 1 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0|

      阶码与尾数之间隐含小数点

可能有个疑问:小数点前面的1去哪里了?由于尾数部分是规格化表示的,最高位总是“1”
我们将尾数定义为M=1+f,小数字段被描述为小数值f,其中0<=f<1,其二进制表示为:
IEEE浮点表示
也就是二进制小数点在最高有效位的左边。
我们也可以把M看成一个二进制表达式为下图的数字,也总能调整阶码使得尾数M的范围在1到2之间。
IEEE浮点表示_2

既然第一位总是1,那就可以直接隐藏不需要显示出来,同时也能够获得一个额外的精度位。

非规格化的值

当阶码全为0时,所表示的数非规格化的形式。这种情况下,阶码的值是1-Bias,尾数M=f,也就是小数字段的值,不包含隐含开头的1。
非规格化有两个用途:

  • 一是提供一种表示数值0的方法,因为使用规格化的数我们必须使M>=1,无法表示0。
  • 二是表示那些非常接近0的数。

拓展

乘2取整法

考虑一个十进制小数0.123,我们可以用“乘10取整”法得到它的每一位小数:第一位小数是0.123 10=1.23,取整数1;第二位小数:0.23 10=2.3,取整数2

上面的方法供你直观理解,下面我们从数学的角度分析其中的原理。

现在有一个十进制小数为0.625,要把它转换为二进制小数,我们需要找到它的每一位。记这个二进制小数点后第1位是$a_1$,第二位是$a_2$,……,那么这个小数的值就是$ a_1 \ast {\frac{1}{2}}^{-1}+a_2 \ast {\frac{1}{2}}^{-2}+a_3 \ast {\frac{1}{2}}^{-3}+… $ 。现在我们的目标是根据0.625找到对应的$ a_1,a_2,a_3$ ,…使得$ 0.625=a_1 \ast {\frac{1}{2}}^{-1}+a_2 \ast {\frac{1}{2}}^{-2}+a_3 \ast {\frac{1}{2}}^{-3}+… $

在等式两边同时乘以2,得到$1.25=a_1 \ast {\frac{1}{2}}^{0}+a_2 \ast {\frac{1}{2}}^{-1}+a_3 \ast {\frac{1}{2}}^{-2}+…$

我们发现,左边的整数部分1对应右边的$a_1$,也就是二进制小数的第一位,于是$a_1=1$,对于剩下的部分:

$0.25=a_2 \ast {\frac{1}{2}}^{-1}+a_3 \ast {\frac{1}{2}}^{-2}+…$

我们再次乘以2,得到$0.5=a_2 \ast {\frac{1}{2}}^{0}+a_3 \ast {\frac{1}{2}}^{-1}+… $于是$a_2=0$

再乘以2,得到$1=a_3 \ast {\frac{1}{2}}^{0}+…$, 于是$a_3=1$,到这里,所有的数都消耗完了,我们找到了0.625对应的二进制小数:0.101

参考文章:浮点数的二进制表示(IEEE 754标准)