计算机如何存储浮点数

计算机如何存储浮点数_第1张图片
浮点数比较结果如上图,为什么会不一致呢?

IEEE 浮点标准

根据IEEE 浮点标准,任意一个二进制浮点数V可以表示成下面的形式:

V = (-1)^s × 2M × 3^E

1.符号(sign) s表示符号位,当s=0,V为正数;当s=1,V为负数。
2.尾数(significand) M是一个二进制小数,1≤M<2。
3.阶码(exponent) E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)

比如十进制的11.0,写成二进制就是1011.0,用IEEE标准表示就是(-1)^0 × 1.011 × 2^3 ,s=0,M=1.011,E=3。

那么,计算机是如何存储s,M,E这三个值呢?如果是一个单精度(32位)浮点数,计算机会在内存中开辟一个32位的存储空间,最高1位保存s,中间8位保存E,最后23位保存M。
计算机如何存储浮点数_第2张图片
如果是一个双精度(64位)浮点数,则开辟64位的存储空间,最高1位保存s,中间11位保存E,最后52位保存M。
计算机如何存储浮点数_第3张图片

  • 对于符号s,存储值非0即1
  • 对于尾数M只保存后面的小数部分。这是由于1≤M<2,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,这样做的好处是可以节省一位有效数字。
  • 而对于阶码E,情况较为复杂。首先,E要通过中间值换算得到真实值。这是由于E要能够表示负数,也就是负次幂。而E本身是无符号的,因此IEEE浮点标准规定,在计算真实值时,E要减去一个中间值。单精度情况下,E减去127,双精度情况下,E减去1023。以二进制数1011.0为例,E的真实值是3,但是存储在计算机中是3+127=130(单精度),换算成二进制就是10000010。

精度丢失

了解了原理,现在我们可以开始分析精度丢失的原因,下面我们来模拟计算机的运算过程。

  • 第一步,将十进制结果转换成二进制。文章开头的例子中,十进制结果是2.7,由于2.7无法用二进制精确表示,因此出现第一次精度丢失。

    2.7 => 10.10110011001…
    
  • 第二步,用IEEE标准表示二进制浮点数,得到s=0,M=1.010110011001…,E=1。

    10.1011001… => (-1)^0 × 1.01011001… × 2^1
    
  • 第三步,按照IEEE标准保存数据。此时是单精度浮点数,M只能保存小数点后23位,多余的部分被丢弃了,因此出现第二次精度丢失。下面是计算机中实际保存的结果。

    1位 8位 23位 丢弃
    0 10000000 01011001100110011001100 11001…
  • 第四步,从内存中取出二进制数值,还原成十进制后显示在屏幕上。由于前面已经经历了两次精度丢失,因此还原出来的结果也就不正确了。

如何避免精度丢失

  1. 使用整数替代浮点数。二进制整数可以完整的表示所有十进制整数,不存在精度丢失问题,因此我们可以将小数位数固定或者较少的数字转换成整数存储。比如存储货币金额,如果存储单位是元,则需要保留两位小数,例如23.45元。如果将单位改成分,则可以完全使用整数存储,例如2345分。
  2. 使用特殊类处理高精度运算。例如JAVA中的Bigdecimal类。不过要注意,使用这些特殊类虽然可以解决精度问题,但有可能带来其它问题,JAVA中的Bigdecimal类在处理性能上就比float和double要低很多。

你可能感兴趣的:(计算机如何存储浮点数)