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

二进制浮点数介绍

二进制组成部分

二进制对应的:

名称 解释
符号位 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)函数。

猜你喜欢

转载自blog.csdn.net/zcjluse/article/details/110141390