含超长浮点值的NumPy数组误判相等,如何正确判定逐元素等价?
这其实是浮点数本身的精度限制导致的——你写的那两个差异在小数点后30多位的小数,在被NumPy默认存储为64位双精度浮点数时,已经被转换成了完全相同的二进制值,所以不管用==、np.array_equal还是np.allclose都会判定它们相等。
为什么会这样?
64位双精度浮点数的有效精度大约是15-17位十进制数字,也就是说,超过这个范围的小数位差异会被“抹掉”,无法被存储和区分。你例子里的两个数值,差异出现在第30多位,远超出了双精度浮点数的分辨能力,所以它们在内存里是同一个值。
正确的解决办法
要准确区分这类超高精度的数值,你需要使用支持更高精度的数据类型,这里有两个可行方案:
1. 使用NumPy的高精度浮点数类型
NumPy提供了np.longdouble类型,它在多数平台上是80位扩展精度浮点数(部分平台是128位),能提供更高的有效精度,足以区分你例子中的数值:
import numpy as np def numpy_equal(): x = np.array([2, 3, 1, 0.000000000000001000000000000000000000002], dtype=np.longdouble) y = np.array([2, 3, 1, 0.000000000000001000000000000000000000001], dtype=np.longdouble) assert not np.array_equal(x, y) # 现在会正确判定两个数组不相等 numpy_equal()
⚠️ 注意:np.longdouble的精度依赖于操作系统和硬件,比如Windows平台上它可能和float64精度一致,这时这个方法就不生效了。
2. 使用Python的decimal模块结合object数组
如果你需要完全可控的超高精度,可以用Python标准库的decimal.Decimal类型,它支持自定义精度,然后将NumPy数组的dtype设为object来存储这些Decimal对象:
import numpy as np from decimal import Decimal def numpy_equal(): # 注意要用字符串形式传入数值,避免浮点数转换时的精度丢失 x = np.array([2, 3, 1, Decimal("0.000000000000001000000000000000000000002")], dtype=object) y = np.array([2, 3, 1, Decimal("0.000000000000001000000000000000000000001")], dtype=object) assert not np.array_equal(x, y) # 正确判定不相等 numpy_equal()
这个方法的优势是精度完全可控,但object类型的NumPy数组运算效率会远低于原生数值类型数组,适合精度要求极高但运算量不大的场景。
补充说明
为什么np.allclose也没起作用?因为allclose是基于浮点数的现有值来计算误差的,而你的两个数值在双精度下已经是同一个值了,所以不管怎么调整rtol(相对误差)和atol(绝对误差)参数,都无法区分它们——必须先确保存储时能保留足够的精度差异才行。
内容的提问来源于stack exchange,提问作者Samudranil Roy

