为什么 0.1 + 0.2 会等于 0.30000000000000004

二进制浮点数为什么会让 0.1 加 0.2 得出 0.30000000000000004,以及本计算器如何将其舍入为整洁的 0.3。

在几乎任何使用二进制浮点数运算的计算器中输入 0.1 + 0.2,得到的原始结果都是 0.30000000000000004,而不是 0.3。本计算器在内部会得到完全相同的值,只是在显示前对其做了舍入处理,所以你看到的是 0.3。问题的根源在于进制不匹配:人类习惯用十进制书写数字,而计算机以二进制存储数字,0.1 在二进制中根本无法精确表示。

要点速览

  • 在二进制浮点数中,0.1 + 0.2 在任何编程语言里都是 0.30000000000000004。
  • 0.1 用二进制表示是一个无限循环小数:0.0001100110011…。
  • 本计算器将结果舍入到 12 位有效数字后再显示,所以显示的是 0.3,而不是原始的双精度值。
  • 0.1 实际存储的值是 0.10000000000000000555111512312578…。

多出来的那些数字从何而来

你应该已经知道,有些分数在十进制下无法精确表示。三分之一是 0.333…,无限循环,因为 3 不是 10 的因数。计算机以二进制存储数字,而二进制中能精确表示的分数,其分母必须是 2 的幂:二分之一、四分之一、八分之一……十分之一不在其中。用二进制表示,0.1 是 0.0001100110011…,无限循环,就像三分之一在十进制下无限循环一样。

双精度浮点数只有 53 位用来存放有效数字,所以那个无限展开的二进制序列必须被截断。当你输入 0.1 时,实际存储的值是 0.1000000000000000055511151231257827021181583404541015625。它非常接近 0.1,但并不等于 0.1。把两个这样带有微小偏差的值相加,误差不会互相抵消,反而会叠加,结果就比 0.3 多出了一点点,于是得到 0.30000000000000004。这是 IEEE 754 标准的行为,几乎所有处理器和编程语言都遵循这一标准,因此在 JavaScript、Python、C 和 Excel 中都会看到同样的结果。

本计算器如何处理这个问题

本站的运算约定在浮点数运算指南中有详细说明:每个结果在显示前都会被舍入到 12 位有效数字。双精度浮点数大约能保证 15 到 17 位可信数字,因此在第 12 位处舍入,既保留了所有可信的数字,又刚好去掉了积累误差所在的那几位。

示例一:动手输入。 在主计算器中输入 0.1+0.2,显示结果为 0.3。底层的双精度值其实是 0.30000000000000004,只是显示规则将其隐藏了。反过来试一次,输入 0.3-0.2:原始结果是 0.09999999999999998,而显示出来是 0.1。乘法同样如此:1.1*3 内部保存的值是 3.3000000000000003,显示为 3.3。

当结果本身确实无法给出精确值时,计算器不会假装它可以。输入 1/3,显示的是 0.333333333333,十二个 3 之后就停止了。有效数字指南详细解释了这种舍入规则和显示位数限制的原理。

什么时候这种差异真正重要

对于日常算术、作业以及工程估算而言,12 位可信数字远远超过任何实际测量所需要的精度。真正需要留意的只有三种较窄的情况,浮点数运算指南中都有说明:

  • 金额计算。不要把货币金额存储为浮点数。以便士、分这样的最小整数单位存储才是安全的做法,累计的微小误差是会计系统中一类已知的常见错误。
  • 代码中的相等性判断。直接判断两个计算得出的浮点数是否相等并不可靠,应改用一个很小的容差范围来比较。
  • 长链式运算。误差会不断累积,而两个非常接近的数相减,会损失最多的精度。

示例二:精确分数。 当你需要一个绝对精确的分数结果时,分数计算器会将分子和分母都保存为整数,绝不做舍入。在那里输入 1/3 + 1/3 + 1/3,结果恰好是 1。而用显示出来的小数做同样的加法,0.333333333333 + 0.333333333333 + 0.333333333333,结果是 0.999999999999。思路相同,但结果截然不同。

示例三:开方与平方。 sqrt(2)^2 是一个经典的陷阱。从逻辑上讲,2 的平方根再平方应该还是 2。但在计算器中,原始结果是 2.0000000000000004,显示时被舍入为 2。当你需要精确值时,应保留根号形式,只在最后一步才求值,计算器函数参考在介绍 √ 键的部分对此有详细说明。

此外还有一个值得了解的整数边界。在 2^53 − 1 = 9,007,199,254,740,991 以内,每一个整数都能被精确表示,计算器会打印出它的全部数字。超过这条界线后,像 19! 这样的值会切换为科学记数法显示,而不是假装自己拥有实际上并不存在的精度。

常见问题

0.1 加 0.2 到底等不等于 0.3?

在精确的十进制运算中,等于。但在二进制浮点数中,存储的结果是 0.30000000000000004。本计算器在内部得到的是同一个值,然后将其舍入到 12 位有效数字再显示,所以你看到的是 0.3。

这是计算器的一个 bug 吗?

不是。这是几乎所有主流处理器和编程语言都实现的 IEEE 754 标准。那些看似“避开”了这个问题的程序和计算器,其实只是用显示舍入把它隐藏了起来,本站的做法也是如此。

怎样才能得到精确的答案?

对于有理数运算,可以使用分数计算器,它绝不做舍入;或者在表达式中一直保留 sqrt(2)、pi 这样的符号形式,直到最后一步才求值。

为什么 1 除以 3 只显示十二个 3?

因为三分之一在十进制和二进制下都无法精确表示。计算器会显示十二位可信数字后就停止,而不是假装这个值最终会终止。