np.sum()与原生sum结果异常:为何float64精度反而不如float16?
NumPy数组求和的精度异常问题
我遇到一个NumPy数组求和的异常情况:数组包含正负数值,用np.sum()、Python原生sum()、循环累加、math.fsum()计算float64类型数组时,返回的都是极小的异常值;但转为float16类型后,sum()和np.sum()却能得到看似"正常"的非零结果。为什么精度更高的float64反而出现这种问题?
测试数据与代码
import numpy as np import math data = np.array([-0.90569317, -1.46995447, -1.74996384, -1.8867866 , -1.87511954, -1.57071542, -1.28009964, -0.93220921, -0.51855901, -0.20460912, -0.44537475, -0.2321858 , 0.22495071, -0.07839277, -0.19347238, -0.37325112, -0.19082078, -0.14362223, 0.17350959, 0.29018016, 0.67731432, 0.40419913, 0.16290318, -0.30590038, -0.97622584, -1.19896056, -1.76534314, -1.90852975, -1.84276997, -1.59776178, -1.40631598, -1.03456112, -0.05293738, 0.15388772, -0.36582663, -0.30218814, 0.71072453, 0.13691746, -0.16483506, -0.15157704, -0.14892544, 0.20161659, 0.27904342, 0.99126422, 1.30786572, 0.55003734, 0.78496944, 0.1353265 , -0.47825463, -0.91682991, -1.62692942, -1.77435859, -1.76799475, -1.68844663, -1.37343609, -1.03562176, -0.11074234, 0.27798278, -0.12877325, -0.03172455, 0.77754495, 0.47685307, -0.012633 , -0.01952717, -0.02376973, 0.06108159, 0.37980437, 1.32324502, 1.50355408, 1.24634851, 1.48021997, 1.12914762, -0.04869481, -0.74023309, -1.32464658, -1.56541221, -1.5574574 , -1.54632066, -1.43866555, -0.95448269, -0.12612164, 0.28593759, 0.07911249, 0.00857983, 0.78762104, 0.55693151, 0.09024923, -0.03596711, 0.41692683, 0.22017782, 0.62110032, 1.18111905, 1.67378705, 1.31369925, 1.48817478, 1.61280016, 2.02963229, 0.41586618, -0.54454473, -1.28116028, -1.41055855, -1.45881774, -1.40896758, -0.74341502, -0.42097999, 0.02714106, -0.17915372, 0.56912889, 1.50726633, 1.13922371, 0.59670557, 0.62216096, 0.67360208, 0.83428927, 0.94353535, 2.18554659, 2.3032778 , 1.39748993, 0.95149016, 0.63223706, 1.51522114, 1.68757539, 1.113238 , 0.13691746, -0.50477067, -0.72591443, -1.1411556 , -1.19789992, -0.90675382, -0.41514646, -0.16218346, 0.06479383, 0.69057234, 0.40366881, 0.15123612, 0.08494602, 0.23555712, 0.5770837 , 0.79345457, 1.31475989, 1.56188937, 1.00929512, 1.11376832, 0.9679301 , 2.25183669, 2.21577488, 1.60802727, 0.38775918, -0.2067304 , -0.54242345, -1.15547426, -1.19153607, -1.1284279 , -0.9751652 , -0.55037826, -0.33824995, 0.21169269, -0.00308722, -0.2629444 , 0.03297459, -0.10543913, -0.12028811, 0.30237754, 0.67148079, 1.29195609, 0.64920732, 0.48427756, 0.14752387])
不同方式的求和结果
- 循环累加结果:
1.2878587085651816e-14 math.fsum()结果:-9.367506770274758e-17
float16类型求和
print(sum(data.astype(np.float16))) print(np.sum(data, dtype=np.float16))
输出:
0.0011463165283203125 0.001146
float64类型求和
print(sum(data.astype(np.float64))) print(np.sum(data, dtype=np.float64))
输出:
1.2878587085651816e-14 0.0
问题原因解析
这不是float64的精度"失效",反而恰恰是它高精度的体现:
- 真实求和值接近0:数组中正负数值的总和本身就非常接近0,float64能保留这些极小的误差值,而float16因为精度有限,累加时会丢失大量小数位信息,把原本接近0的结果"凑"成了看似非零的小数值。
- 浮点数累加的误差特性:浮点数累加时,大数会吞噬小数的精度,但你的数组正负值相互抵消后,剩余值本身极小,float64能准确反映这个结果;float16因位数不足,无法区分极小值和真实非零值,返回的非零数实际是不准确的近似值。
- 累加算法差异:
np.sum处理float64时可能使用成对累加等优化算法,进一步减少误差,所以直接返回0.0;原生sum和循环累加的误差稍大,返回极小非零值;math.fsum是专门优化浮点数累加误差的算法,返回的误差值更小。
简言之:float64返回的极小值才是更接近真实总和的结果,float16的非零结果是精度不足导致的虚假"正常值"。
内容的提问来源于stack exchange,提问作者LordNR
相关产品推荐
相关产品推荐

