为什么不同的计算机里的浮点数会不一样
最近有被问到“你知道为什么不同计算机里的浮点数会不一样吗”
“不太清楚”
“你没有好奇去不同的机器上尝试打印出来吗”
“没有...”
很惭愧,发现自己对这些计算机底层原理还不是很熟,并且自己也没有实际的去尝试过好奇过,人呐,还是要对知识的追求继续保持好奇态度的!
先简单的做个测试,我在mac上计算0.1+0.2,发现出来的值不是0.3,而是0.300000000004. 这不是什么 bug,也不是 Python 有问题,而是浮点数在做运算时必然的结果!
首先我们先看一下浮点数的二进制结构是什么样的,从下图可以看到,第一个是符号位,后面5位是指数区域,剩下的10位就是尾数区域
IEEE 754浮点数格式的科学计数法格式: N = (-1)s* 1.f * 2e
8.5 的例子可以表示为 23 + 1/2 ,是因为 8 和 0.5 刚好都是 2 的次方数,所以完全不会产生任何精准度问题。但如果是 8.9 的话因为没办法换成 2 的次方数相加,所以最后会被迫表示成 1.0001110011… * 23,而且还会产生大概 0.0000003 的误差。这样的误差积少成多,在操作频繁之后就会累积起来越来越大,导致误差明显。比如帧同步中一致性的问题。
总结
为什么 0.1 + 0.2 != 0.3 呢?首先,0.1 和 0.2 这两个实数无法用二进制精确表示。在二进制的世界里,只有包含因子 5 的十进制数才有可能精确表示,而 0.1 和 0.2 转换为二进制后是无限循环小数,在计算机中存储的值只能是真实值的近似表示,这里是第一次精度丢失;其次,计算机浮点数采用了 IEEE 754 标准格式,IEEE 754 严格规定了尾数域和指数域可表示的大小,位数有限,意味着可表示的信息量是有限的,换句话说就会存在三种误差:上溢、下溢和舍入误差。而 0.1 + 0.2 的结果的尾数域部分刚好超过了尾数域位数,超过位数的部分舍去,存在舍入误差,这里是第二次精度丢失
如何减少误差呢
- 在某些语言会提供所谓的 epsilon,用来让你判断是不是在浮点误差的允许范围内,以 Python 来说 epsilon 的值大约是 2.2e-16
- 直接用十进制来计算
Reference:
- https://juejin.cn/post/6860445359936798734