python - 在 Cython 中添加双打时精度损失
问题描述
我目前正在尝试使用 Cython 优化 Python 代码。我需要输出完全相同,但我希望精确度有问题。据我了解,Python 具有无限的精度,而 Cython 的“Double”相当于 Python 浮点数。我正在努力使用下面的函数(不允许共享代码 - 它是一个具有类似结构的虚拟函数):
def dummyfunction(c_np.ndarray[double, ndim=1] dummyarray, int a, int b, const c_np.uint8_t[:,:] dummyimg):
cdef double q = 0.111
cdef double w = 0.222
cdef double e = 0.333
cdef double[:] dummyview = dummyarray
cdef int i, j
cdef int r, g, b
for i in range(a):
for j in range(b):
r = dummyimg[j][0]
g = dummyimg[j][1]
b = dummyimg[j][2]
dummyarray[i * b + j] = (
q * r
+ w * g
+ e * b
)
dummyarray[:] = dummyview #i'm updating a class attribute in place
我试过打印'q r'、'w g'和'e*b'。这些产品的精度与 Python 中的相同!麻烦在于将这三个值相加。它只留下三位小数。我觉得这是因为在大多数情况下,被总结的 3 个组件之一只有 3 个小数位(例如 35.879999999999995、51.068999999999996、9.348)。不过,Python 似乎将其总结为更高的精度(即 96.29699999999998 与 96.297)。
有什么建议吗?
解决方案
首先,Python 对于整数数学只有任意精度。对于浮点数学,Python float 是 IEEE double=precision(64 位)值,就像 Cython double 一样。
假设您在 x86(或 x86-64)平台上,可能有几个罪魁祸首。x86 架构为浮点数学提供了两种不同的指令集。经典路径使用 x87 指令集,所有计算实际上都是以 80 位(又名“long double”)精度完成的。当一个值(中间值或最终值)存储到内存时,它会被截断为 64 位精度。只要它保留在 FPU 寄存器中,它就会保持完整的 80 位精度。
另一个可用的指令集使用所谓的 SSE(流 SIMD 扩展),它可以同时对多个操作数进行操作。但是,这些计算仅使用类型的“严格”精度(在本例中为 64 位)完成。
我的猜测是 Python 选择了一个代码路径,而 Cython 选择了另一个。同样可能的是,它们都选择了相同的指令集(很可能是 SSE 指令集),但它们以不同的顺序添加中间产品。由于精度有限,求和的顺序会影响结果的准确性。
另外,请注意,在任何一种情况下,计算都将以至少 64 位精度完成。正如您所说,没有“只有 3 位小数”的计算。请记住,与往常一样,计算是以二进制浮点数完成的,而不是十进制数。这些值之间的真正差异可能只有几个低位。
推荐阅读
- bash - 如何使用 awk 命令在匹配正则表达式之前和之后打印 5 行
- python - 我用什么函数从 Python 中的索引列表中删除某个?
- javascript - 选中另一个框时 JavaScript 取消选中框
- reactjs - 如何使用用户 id 令牌 React Native 和 Firebase 对应存储数据?
- javascript - 数组解构中的 SPREAD 运算符
- apache-spark - Spark UI Executors 选项卡中 --executor-memory 的内存不正确
- python - MySQLdb 在本地工作,但在部署时没有
- python - 如何在 for 循环中只打开一次浏览器?
- kubernetes - Terraform,为什么要使用 Kubernetes 作为额外的层?
- php - 从具有 2 个条件和 3 个表的表中检索信息