欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

二进制浮点数的精度丢失问题

程序员文章站 2022-07-05 22:47:59
...

二进制浮点数介绍

二进制组成部分

二进制对应的:

名称 解释
符号位 1位正数 0位负数
指数位 到0~255 一共8位 映射到 -126~127 0和255(有特殊用途 NAN和无穷大小的判断)
有效位 一共23位

丢失精度的情况1

十进制数0.3+0.6,因为计算机都是先转换成二进制进行计算,所以我们先把0.3和0.6转换成二进制
这里我们直接用转换工具
二进制浮点数的精度丢失问题

0.3的二进制数:

乘二进位决定二进制为为 0或1,小数部分乘以2,大于1则二进制位为1,反之为0。
例:有效数字小数部分转化是0.3×2进制位为1,如果小于1,则二进制位为0,比如0.3×2等于0.6那么第一位就为0,0.6×2=1.2,那么第二位二进制位就为1,然后1.2的小数部分0.2×2又是0,所以第三位二进制位为0… 前三位010
和上面的0.010匹配,有兴趣可以自行计算下去,当然这个计算不可能算完的,因为只要小数部分为0.6,又回到起点,所以这是个无线循环乘法,所以丢失精度是自然的。

因为浮点数的有效长度是23位,所以有效长度为

0.01001100110011001100110

其中0.我们可以忽略掉

下面我们来看0.6的二进制表示

二进制浮点数的精度丢失问题

名称 解释
0.6*2=1.2 1
0.2*2=0.4 0
0.4*2=0.8 0
0.8*2=1.6 1
0.6*2=1.2 1

对应了上面的0.10011 ~10011 其中前头的0.我们可以忽略掉 ,只是表示这个是小数
有效位是23位

0.10011001100110011001100

0.3+0.6结果:

二进制形式:
二进制浮点数的精度丢失问题

十进制形式:
二进制浮点数的精度丢失问题
我们可以看出0.3+0.6等于=0.899999999…
二进制浮点数的精度丢失问题

我们再来看一组数据0.3+0.4

0.3我们就不进行二进制分析了。和上面一样
我们来分析0.4转二进制
二进制浮点数的精度丢失问题

名称 解释
0.4*2=0.8 0
0.8*2=1.6 1
0.6*2=1.2 1
0.2*2=0.4 0
0.4*2=0.8 0

对应了上面的0.01100 ~01100 其中前头的0.我们可以忽略掉 ,只是表示这个是小数,后面的数值可以自行计算

0.4的23位有效二进制如下图

0.01100110011001100110011

此时我们把0.3和0.4 二进制进行相加
二进制浮点数的精度丢失问题
我们将0.3与0.4进行 的二进制进行相加
二进制浮点数的精度丢失问题
二进制浮点数的精度丢失问题
为什么一个是0.7000一个是6.9999 开头的
很明显有问题,经过测试发现是0.3的二进制没有保留位数(二进制保留)即将第二十四位如果为1则直接向前进1(大部分情况是这样),因为0.3的第24位是1,所以0.3转二进制实际为

0.01001100110011001100111

再和0.4相加的结果
二进制浮点数的精度丢失问题
和上面的0.7000000(0.7000000476837158)四舍五入得到。

还有一种丢失精度的情况

我们知道浮点数有效位数为23位,如果两个数相加,或者一个数的有效位超过了23位,那么超过23位的精度将全部丢失
二进制浮点数的精度丢失问题
20000000转2进制,如图 位数一共有25位,最后两位作为科学计数法被抹去了,如果我们此时+1,是不是在

1001100010010110100000000 +1呢?

答案很明确。但是+1没效果,因为一共25位,只会保留23位,但是如果我们+2或者3就有效果了
二进制浮点数的精度丢失问题
加2和3有效果:
效果不如意,因为+2和3会发生进位,第24位为1会进位。有效果是有效果,但是数据可能偏差大
二进制浮点数的精度丢失问题
二进制浮点数的精度丢失问题
下面这个算法能解决精度丢失的问题
Kahan Summation算法

public class KahanSummation {
  public static void main(String[] args) {
    float sum = 0.0f;
    float c = 0.0f;
    for (int i = 0; i < 20000000; i++) {
    	float x = 1.0f;
    	float y = x - c;
    	float t = sum + y;
    	c = (t-sum)-y;
    	sum = t;    	
    }
    System.out.println("sum is " + sum);   
  }	
}

综上所述:浮点数用于一些不需要很准确的计算。如果涉及到钱等一些数据可以用bigdecimal,数据库用decimal(12,2)函数。

相关标签: 计组