浮点数精度是几乎所有编程语言都会遇到的“反直觉”问题,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。