为何Numpy在该计算场景中出现舍入问题?
浮点数精度问题详解:你的代码误差根源解析
先看你的代码:
import numpy as np def calc_negation(negations): takens = 1. - negations return 1. - np.prod(takens, axis=1, keepdims=True) result = calc_negation(np.array([[.1, .1], [.5, .5]])) expected = np.array([[.19], [.75]]) print(result) print(result == expected) # show the failed equality. assert result.shape == (2, 1) assert result.shape == expected.shape assert result.dtype == expected.dtype assert expected[0, 0] == .19 # why is this okay!?! assert expected[1, 0] == .75 assert np.allclose(result, expected) # why is numpy rounding 1 - (.9 * .9)? assert np.array_equal(result, expected) # FAILS!
核心原因:二进制浮点数的固有局限
十进制里的0.1、0.9、0.19这类数,在二进制中是无限循环小数,无法被标准浮点数(Python的float、numpy的float64)精确存储,只能以近似值形式存在。不同计算路径会产生不同的近似结果,这就是所有问题的根源。
1. 为什么result[0,0] != 0.19,但expected[0,0] == .19?
- 当你直接写
.19或用np.array([[.19]])创建数组时,这个值是直接将十进制0.19转换为二进制浮点数的近似值,其精确展开为0.19000000000000000333(保留20位小数)。 result[0,0]是计算产物:- 0.9的二进制近似值是
0.90000000000000002220,两者相乘得到0.81000000000000005551; - 1减去这个值得到
0.18999999999999994449,和直接写的.19的二进制表示完全不同,所以精确相等判断失败。
- 0.9的二进制近似值是
- 而
expected[0,0] == .19成立,是因为两者是同一个浮点数近似值,二进制位完全一致。
2. 为什么误差不是你熟悉的“微小正误差”?
浮点数的近似误差方向由转换/计算路径决定:
- 直接转换十进制0.19时,二进制近似值略大于真实值;
- 通过
1 - 0.9*0.9计算时,0.9的近似值略大于真实值,相乘后结果略大于0.81,1减去它就得到略小于0.19的结果。
误差方向相反,自然就不是你习惯的“0.19000000000004”这类正误差。
3. 为什么np.allclose通过但np.array_equal失败?
np.array_equal是精确匹配判断,要求两个数组的每个元素二进制表示完全一致,所以会失败;np.allclose是近似匹配判断,默认检查元素差是否在浮点数合理误差范围内(相对误差1e-05,绝对误差1e-08),你的两个结果的差远小于这个阈值,所以通过。
解决方案
如果需要避免这类精度问题,可以:
- 使用Python标准库的
decimal模块,支持高精度十进制运算; - 对numpy数组,优先用
np.allclose替代精确相等判断来比较浮点数; - 部分平台可使用
np.float128类型,提供更高精度的浮点数存储。
内容的提问来源于stack exchange,提问作者philosofool
相关产品推荐
相关产品推荐

