当你在代码里写下小数时,就已经有误差了!

120 阅读2分钟

不管你用什么语言,当你有一定开发经验时,都会了解到小数计算时会有误差的现象。
经典案例是 0.2 + 0.1 !== 0.3

这本质是二进制存储方式的锅,已经可以说是人尽皆知,这里就不用展开陈述了。

笔者在这里要强调的是,把十进制转换成二进制的操作是在写下赋值时就执行了,而不是计算时或输出时才做

当你写下了小数,代码运行时就会把你的小数转成二进制,才能放到内存中。谁知道后续你的代码会不会用到这个数字呢?既然写下了一般就是后续要用的。

如果你感觉这是废话,那么说明你是有一些计算机基础的,对于那些直接从弱类型的语言开始学习的开发来说,明白这个道理是很重要的。

根据 IEEE-754 标准,0.3是不能被精确存储为二进制的

让我费解的是很多语言的行为是下面这样:
(以python举例)
直接打印 print(0.3) 是看不到0.3后面的误差的
但是print(0.1 + 0.2)能看到误差

对此,豆包Ai给出解释:

因为语言运行输出打印时把输出结果给“美化”了。

语言运行输出打印时怎么美化的?

很多语言都是保留一些位,后面直接砍断或者四舍五入

  1. 把浮点小数的二进制计算成十进制
  2. 检查十进制小数位是否很长(长度检查取决于不同的语言运行时的底层机制)
  3. 如果过长就保留前几位,后面直接砍断或者四舍五入
  4. 检查砍断后的十进制值,如果末尾有一串0,就把这段0删去(有些语言会保留,比如C )
  5. 输出显示在屏幕上

所以很多时候是这样👇
对于0.3,转为二进制放进内存,再转为十进制用于输出时展示 ,其误差太小,被代码运行时省去了
对于0.1+0.2,是先把两个数都转为二进制,再进行相加,再把结果转回十进制,此时转换后的值和直接打印0.3是不一样的。

这有点类似于谷歌翻译把一句中国话翻译成英文再翻译成中文,和原来的中文内容不完全一样。

以后可以提醒自己,当你写出0.2的时候,误差就已经存在了,而不是表面看起来那样打印输出0.2真的会打印输出0.2,让人直觉上看起来0.2没误差。其实是有的,只是过于微小被你的代码运行时“美化”了

建议各读者问一下Ai自己平常用的开发语言会如何处理小数的“美化”,会处理多少位,选择四舍五入还是直接截断。