numpy数组元素检查的异常行为问题排查
异常根源
这个现象完全是二进制浮点数的存储精度与累积计算误差导致的严格相等校验失效,不存在代码逻辑错误。
偏差的具体产生链路
- 构造的数组
b = 3*np.array([0.5,1,1.5])中,0.5、1、1.5都是可以被IEEE754双精度浮点数精确表示的数值,乘3之后得到的1.5、3.0、4.5也是完全精确的,没有任何存储误差。 - 数组
a的生成逻辑是先通过np.arange(0.5,1.6,0.1)生成序列再整体乘3。核心问题点是:十进制的0.1无法用有限位的二进制浮点数精确存储,实际存在e-17量级的固有误差;arange生成序列时是反复把这个带误差的0.1做累加,每一步的数值都会继续累积浮点偏差,最终乘3之后的元素,哪怕十进制打印出来和b的元素完全一致,实际二进制存储值也存在细微差异。 - 执行
c in b判断时,本质是逐元素做严格的二进制相等比较,只要两个值的存储位差了最小单位的偏差,就会判定为不相等。 - 最终只有1个元素能匹配的原因:
a的第一个元素是3*0.5,0.5本身是二进制可精确表示的数值,和b的第一个元素存储完全一致;剩下两个预期匹配的元素,因为之前步长累加的误差累积,实际值和b中的精确值存在1e-15量级的偏差——比如a中预期等于3.0的元素实际存储值为2.9999999999999996,日常打印输出时因为默认截断显示精度,这个微小差异会被隐藏,才会出现“看起来数值一样但匹配失败”的反常结果。
正确的处理方案
- 浮点数相等判断不要使用严格的
in、==比较,换用带容差的判断逻辑np.isclose(),允许两个数值在约定的误差范围内视为等价,示例:
for c in a: print(np.any(np.isclose(c, b)))
- 生成固定间隔的浮点数序列时,优先用
np.linspace替代np.arange,前者基于起止点和等分数直接计算每个位置的数值,不会出现步长反复累加的累积误差 - 如果必须使用
arange生成序列,可以对最终结果做一次低精度规整,比如用np.round(a, 9)消除e-16量级的浮点误差,再做相等判断
内容的提问来源于stack exchange,提问作者MasonTep
相关产品推荐
相关产品推荐

