GUIDE 07

计算器的浮点误差:为什么 0.1+0.2 和 sin(π) 看起来不对

讲解 IEEE 754 双精度下 0.1+0.2 为何等于 0.30000000000000004、12 位显示如何呈现为 0.3、sin(π) 的修正、170! 与溢出、2⁵³ 的整数上限,以及相减抵消误差。

在大多数编程语言中输出 0.1 + 0.2,会得到 0.30000000000000004。这不是故障,而是计算机存储实数的方式所致。本指南讲解 SciKey 和大多数数值软件使用的 IEEE 754 双精度格式、它的局限如何体现在结果中,以及计算器如何处理这些问题。

1. IEEE 754 双精度

双精度(binary64)使用 64 位。

部分位数作用
符号1正或负
指数112 的几次幂
尾数52有效数字(加上隐含的首位 1,实际为 53 位)

53 位有效位约相当于 15~17 位十进制数字。可表示的最大值约为 1.797×10³⁰⁸,1 与下一个双精度数之间的差(机器 epsilon)为 2⁻⁵² ≈ 2.22×10⁻¹⁶。

2. 0.1 无法用二进制精确表示

正如 1/3 = 0.333… 在十进制中永远写不完一样,1/10 在二进制中也写不完:0.1 = 0.0001100110011…₂,无限循环。它被舍入到 53 位,所以实际存储的值是

0.1000000000000000055511151231257827…

0.2 也被存得略大。两者之和带着两份误差,成为 0.3000000000000000444…,这与存储 0.3 的双精度数(0.2999999999999999888…)是不同的双精度数。因此能唯一表示这个和的最短十进制数是 0.30000000000000004。

出于同样的原因,0.1×3 也是 0.30000000000000004。像 0.5、0.25、0.375 这样分母为 2 的幂的小数,在二进制中可以精确表示,没有误差。

3. 为什么计算器仍显示 0.3

SciKey 并不直接显示内部原始值,而是按您设定的有效位数(默认 12 位)舍入后显示。0.30000000000000004 舍入到 12 位是 0.300000000000,也就是 0.3。误差位于第 17 位,根本到不了显示位。

实体科学计算器也是如此:通常内部用 15 位左右计算、显示 10 位,在显示阶段隐藏末尾的微小误差。改变的只是显示,存储的值仍是 0.30000000000000004。继续用它计算,误差通常在 10⁻¹⁶ 左右,实际应用中无关紧要。

4. 为什么 sin(π) 不等于 0

在 RAD 模式下,sin(π) 本应恰好为 0。但 π 是无理数,最接近 π 的双精度数比真实值小约 1.22×10⁻¹⁶。在 π 附近正弦的斜率为 −1,所以 sin(双精度 π) ≈ 1.2246×10⁻¹⁶。正弦本身算得很准确,是输入从一开始就不是精确的 π

SciKey 按以下规则进行修正:

  1. 在 DEG 和 GRAD 模式下,参数按 360°(400 gon)精确约化,90° 的倍数返回精确值:sin(180) = 0,cos(90) = 0,tan(90) 无定义,因此返回错误。
  2. 在 RAD 模式下,若参数与 π 的整数倍的相对误差在 10⁻¹³ 以内,sin 返回 0;若与 π/2 的奇数倍一致,cos 返回 0,tan 返回错误。

所以在 SciKey 中 sin(π) 显示为 0。这种修正只在 π 的倍数附近生效,不影响其他任何计算。

5. 大数:阶乘与溢出

阶乘增长极快。170! ≈ 7.25741561531×10³⁰⁶ 还能放进双精度,但 171! 约为 1.24×10³⁰⁹,超过了最大值 1.797×10³⁰⁸。因此 SciKey 的 n! 接受 0~170 的整数,171! 会返回溢出错误。

实际上,大阶乘通常出现在比值中。组合数 C(200, 3) 是 200!/(3!·197!),而 nCr(200, 3) = 1313400 可以直接算出,不经过巨大的中间值。在概率计算中,取对数后相加也是常用的方法。

6. 整数上限:2⁵³

由于有 53 位有效位,不超过 2⁵³ = 9007199254740992 的所有整数都能精确表示。超过之后,双精度数会跳过一些整数。2⁵³ + 1 无法表示,会被舍入回 2⁵³,所以 2^53+1-2^53 得 0 而不是 1:加上的 1 在存储和时就消失了。

这就是 SciKey 只对绝对值不超过 2⁵³ 的整数显示二、八、十六进制的原因;超过后低位数字无法保证。也正因如此,超过 16 位的卡号或 ID 被当作数字处理时,最后几位会发生变化。

7. 相减抵消:最危险的误差

两个几乎相等的数相减时,高位数字相互抵消,剩下的几乎全是误差。这称为相减抵消(灾难性抵消)。

例 1. (1+1E-15)-1 本应为 10⁻¹⁵,双精度下却得到 1.11022302463×10⁻¹⁵。存储 1 + 10⁻¹⁵ 时引入了 10⁻¹⁶ 量级的舍入误差,减去 1 后,这个误差占结果的 11%。

例 2. 当 x = 10⁸ 时,√(x²+1) − x 约为 5×10⁻⁹。直接计算时,√(10¹⁶+1) 与 10⁸ 无法区分,结果为 0。有理化后可以得到不含减法的等价形式:

√(x²+1) − x = 1 / (√(x²+1) + x)

把 10⁸ 存入 x,1/(sqrt(x^2+1)+x) 就能准确返回 5×10⁻⁹。

例 3. 1 − cos(10⁻⁸) 约为 5×10⁻¹⁷,但 cos(10⁻⁸) 被舍入为恰好 1,直接计算结果为 0。利用恒等式 1 − cos x = 2 sin²(x/2),2sin(0.5E-8)^2 可得 5×10⁻¹⁷(RAD 模式)。

显示再多位数也无法解决这个问题。必须改写公式。

8. 实用技巧

  • 不要判断严格相等。 比较两个计算结果时,检查它们的差是否足够小(例如相对误差 10⁻¹²)。
  • 金额用整数单位处理。 以最小货币单位的整数计算,小数误差就不会累积。
  • 避免相减两个几乎相等的数。 通过有理化、恒等式或变形来消除减法。
  • 大数用比值处理。 阶乘或乘方过大时,使用 nCr、对数,或先约分。
  • 不要舍入中间结果。 双精度误差在 10⁻¹⁶ 左右,但手动舍入到 4 位会引入 10⁻⁴ 左右的误差。更大的误差通常来自人,而不是机器。
  • 试着把显示调到 15 位。 如果结果看起来奇怪,增加显示位数查看内部值,分辨是末位误差还是公式本身有误。

→ 打开计算器 函数参考

最后更新:2026-09-24