人人都会AI编程

30.7 浮点数精度问题

更新时间:2026-07-12

浮点数精度是几乎所有编程语言都会遇到的“反直觉”问题,Python 也不例外。如果你没留意过它,迟早会在比较两个看似相等的小数时踩坑。

现象:为什么 0.1 + 0.2 不等于 0.3?

在 Python 解释器中执行:

>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

这个结果会让初学者惊讶,但它并非 Python 的 bug,而是 IEEE 754 双精度浮点数标准 的必然表现。

原因:二进制无法精确表示部分十进制小数

计算机内部以二进制形式存储数字。整数和像 0.5、0.25 这样能用 2 的负次幂精确表示的十进制小数,在二进制中是精确的:

  • 0.5(十进制) = 2⁻¹ → 二进制 0.1
  • 0.25(十进制) = 2⁻² → 二进制 0.01

但 0.1 和 0.2 这类数,在二进制中是无限循环小数,就像十进制无法精确表示 1/3(0.3333…)一样。Python 浮点数只有 53 位二进制精度(约 15-17 位有效十进制数字),超出部分会被截断,于是产生了微小的舍入误差。0.1 + 0.2 的计算结果实际上是两个近似值相加后的近似值,与 0.3 的近似值并不完全相等。

常见踩坑场景

1. 用 == 直接比较浮点数

# 错误做法
if 0.1 + 0.2 == 0.3:
    print("相等")   # 永远不会执行

2. 浮点数作为字典的键

因为小数有微弱误差,很可能导致你认为应该相等的键无法命中,产生难以排查的 bug。

3. 累加造成误差放大

total = 0.0
for _ in range(1000):
    total += 0.1
# 期望 100.0,实际接近但不精确

4. 结果格式化时出现长尾数

print(1.1 * 1.1)   # 1.2100000000000002

解决方案:视场景选择正确工具

方案一:使用 math.isclose() 进行近似比较(推荐)

import math
if math.isclose(0.1 + 0.2, 0.3):
    print("近似相等")   # True

# 可设置相对/绝对容差
# math.isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

方案二:使用 Decimal 进行精确十进制运算

适合金融、财务等对精度要求极高的场景。

from decimal import Decimal
a = Decimal('0.1')   # 必须用字符串传入,避免先产生浮点误差
b = Decimal('0.2')
print(a + b)         # 0.3,精确相等

注意:Decimal 运算速度比普通浮点慢,只在必要时使用。

方案三:将小数转化为整数运算

例如以“分”为单位替代以“元”为单位的浮点数计算。

# 处理金额时用整数(单位:分)
price = 99   # 9.9 元
count = 3
total = price * count   # 297,即 2.97 元

方案四:仅在展示时格式化

如果浮点误差不影响计算结果(例如只是最终显示),可以在输出时用 round() 或格式化字符串控制小数位数:

value = 0.1 + 0.2
print(f"{value:.2f}")   # 0.30

但注意 round() 同样受二进制表示影响,遇到“.5”时可能不符合直觉四舍五入(称为“银行家舍入”),仅适用于展示,不应用于精确比较。

小结

  • 浮点数精度问题不是 Python 的错,是所有使用 IEEE 754 标准的语言共有的限制。
  • 需要精确小数的场景(如金钱)请用 Decimal 或整数过渡。
  • 常规判断相等用 math.isclose() 替代 ==
  • 格式化输出可以掩盖显示上的长尾数,但不能解决实质比较问题。

理解这一点后,你就能避免绝大多数因浮点精度引发的隐性 bug。