pytest.approx 是最省心的浮点比较方案,支持 abs/rel 双模式并自动处理边界;math.isclose 适合需布尔返回值的场景;junit delta 需动态计算防负值;手写工具须单独处理 nan 和 0。

直接用 pytest.approx 是最省心的方案
如果你用的是 pytest,别自己写容差逻辑。pytest.approx 内置了相对误差(rel)和绝对误差(abs)双模式,自动处理边界情况,比如 0.0 或极小值。
常见错误是只传一个数字当 tolerance,比如 assert a == pytest.approx(b, 1e-6) —— 这实际是把 1e-6 当作 abs 参数,但写法易误解。明确写出来更安全:
-
assert result == pytest.approx(expected, abs=1e-9):固定绝对误差,适合量级稳定(如坐标、时间戳) -
assert result == pytest.approx(expected, rel=1e-3):相对误差,适合跨数量级(如科学计算中 0.001 和 1000 都要测) - 两者可共存:
pytest.approx(expected, abs=1e-12, rel=1e-5),取其中更宽松的那个生效
math.isclose 适合需要显式布尔判断的场景
当你不能抛异常、而要自己控制流程时(比如在测试 setup 中预检数据,或封装断言工具函数),math.isclose 更合适。它返回 True/False,不触发 AssertionError。
注意它的默认参数:rel_tol=1e-09, abs_tol=0.0。如果比较含 0 的值(如 math.isclose(1e-10, 0.0)),必须显式设 abs_tol,否则会失败:
-
math.isclose(a, b, abs_tol=1e-10):强制启用绝对容差 -
math.isclose(a, b, rel_tol=1e-6, abs_tol=1e-12):兼顾大小值,推荐组合 - 别依赖默认
abs_tol——它是 0,对近零值毫无意义
JUnit 的 assertEquals delta 必须为正,且慎用 Math.min
Java 用户常误用 Math.min(expected, actual) 当 delta,这在值为负或一正一负时直接出错:delta 变成负数,JUnit 报 IllegalArgumentException。
正确做法是基于量级动态算 delta:
- 用
Math.max(Math.abs(expected), Math.abs(actual)) * 1e-3模拟相对误差 - 下限兜底:
Math.max(1e-9, Math.max(...) * 1e-3),避免 delta 趋近于 0 - 固定值仅限调试用,比如
1e-6适用于单精度图形计算,但别用于金融或物理仿真
自己写断言函数时,别忽略 0 和 NaN 的特殊处理
手写 assert_float_equal(a, b, tol) 类工具时,最容易漏掉两个点:
-
a或b是NaN:IEEE 规定NaN != NaN,所以得先用math.isnan()单独判,否则abs(a-b) 永远是 <code>False -
a和b异号但都很小(如-1e-10和1e-10):此时相对误差失效,必须 fallback 到绝对误差 - Python 中
float('inf')和-float('inf')也要单独处理,math.isclose已覆盖,但自定义逻辑常遗漏
真正难的不是算误差,而是想清楚「这个浮点数在业务里代表什么」——是温度(±0.1℃ 就够)、还是资金(必须精确到分)、还是模型输出(相对误差 1% 可接受)。delta 值从来不是技术参数,而是业务契约。










