我正在尝试在整个操作系统上保持一致的浮点计算结果,并且在较新的系统上进行测试时遇到了关于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十六进制表示。这似乎是具体的。
我的工作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(对于arcsinh为3.0)是正确的,因为它应从零舍入。但是,结果一致的解决方案对我来说更重要。
谢谢您的时间。
答案 0 :(得分:2)
所以我发现了为什么答案在系统之间是不同的,而且我以前没有看到过。但是,我仍然不确定如何获得一致的结果。
正如Mark Dickinson在评论中指出的那样,我忽略了gcc正在使用MPFR(具有正确舍入的多精度浮点)执行编译时优化。在生成的二进制文件上运行ldd表示libm根本没有动态加载。我用clang重新编译了我的C示例,并在libm
中动态链接,瞧瞧,我得到的结果与在整个系统中通过python / numpy取得的结果完全相同。结果是asinhf(3.0f);
在旧系统上四舍五入,在新系统上四舍五入。
因此,在某种程度上,这看起来像是libm
库的更新。
具体来说,GLIBC的变化至少发生在2.23和2.27之间。
如果有人对在系统之间进行一致的取整有任何建议,我将不胜感激。我怀疑不管旧系统的精度如何,舍入都可能不正确。
谢谢您的时间。