【发布时间】:2019-04-04 21:49:45
【问题描述】:
我试图在操作系统中保持一致的浮点计算结果,但在新系统上进行测试时,我遇到了关于 numpy 和 arcsinh 的奇怪回归。这是一个最小的工作示例,它在不同系统中的行为有所不同。
#!/usr/bin/env python
import struct
from numpy import (array, arcsinh, float32)
def float_to_hex(f):
return hex(struct.unpack('<I', struct.pack('<f', f))[0])
numpy_result = arcsinh(array([3.0], dtype=float32))[0]
print("asinh(3.0):", numpy_result, float_to_hex(numpy_result))
在 Centos 7 和 Ubuntu 16.04 上,我得到以下结果:
asinh(3.0): 1.8184464 0x3fe8c2da
在 Ubuntu 18.04(和一位同事说的 Windows)上,我得到以下结果:
asinh(3.0): 1.8184465 0x3fe8c2db
很高兴了解为什么会发生这种情况以及如何在系统之间获得一致的结果。理想情况下坚持使用 32 位浮点解决方案。是否有一些我忽略了跨操作系统更改的 numpy 选项?
值得注意的是,我无法用 C 程序重现这一点。使用 GLIBC 的 asinh(32 位浮点 3.0)我 总是 得到 1.8184465 的新结果,无论我使用什么系统,它都是 0x3fe8c2db 十六进制表示。这似乎是特定于 numpy 的。
我的工作 C 示例:
#include <stdio.h>
#include <math.h>
int main() {
float value = asinhf(3.0f);
unsigned int hexValue = *(unsigned int *)&value;
printf("Plain value: %.7f\n", value);
printf("Hex value: 0x%8x\n", hexValue);
return 0;
}
我还可以验证跨系统正在使用完全相同的 numpy 版本。在这种情况下,它是 1.15.3。 numpy 包是从各处的轮子安装的,因此安装了相同的共享对象库。为了我的理智,我通过在所有系统上对所有库运行 file 操作来仔细检查这些库。
我相信根据 IEEE 754,5 的最后一个有效数字(对于 3.0 的反正弦)是正确的,因为它应该从零舍入。但是,结果一致的解决方案对我来说更重要。
感谢您的宝贵时间。
【问题讨论】:
-
您是否也看到了 asinh(3) 的 64 位浮点值的区别?
-
你能展示你用于测试的C代码吗?请注意,如果您直接执行
asinh(3.0)之类的操作,则gcc将使用 MPFR 进行完美正确的 compile-time 评估,因此您不会触及 libm 实现asinh在这种情况下。 -
但总的来说,希望在
libm实现中获得可重复的结果是乐观的,即使假设采用 IEEE 754 格式。 NumPy 在此处受操作系统的libm的摆布。 -
我认为来自 Eric Postpischil 的this answer 在这里是相关的:“如果使用数学库例程(例如 cos 和 log),这是另一个问题,因为它们很难很好地计算,而且不同实现提供了不同的近似值。”
-
使用round-to-nearest-ties-to-even,对于
asinh(3),正确舍入的32位float是由0x3fe8c2db(1.81844651699066162109375)表示的,但这不是因为IEEE- 754 说你应该从零四舍五入。舍入规则是用户可选择的,默认值通常是舍入到最接近的可表示值,并与偶数低位相关。
标签: python numpy floating-point operating-system