intel MacBook 和 M1 之间的 np.float32 浮点差异

np.float32 floating point differences between intel MacBook and M1

我最近将我的 Intel MacBook Pro 13" 升级到了配备 M1 Pro 的 MacBook Pro 14"。一直在努力让我的软件重新编译和工作。幸运的是没有大问题,除了一些晦涩的 Fortran 代码和 python 中的浮点问题。关于 python/numpy 我有以下问题。

我有一个很大的代码库,为了简单起见,我将使用这个将飞行高度转换为压力的简单函数来显示问题。

def fl2pres(FL):
    P0=101325
    T0=288.15
    T1=216.65
    g=9.80665
    R=287.0528742
    GAMMA=0.0065
    P11=P0*np.exp(-g/GAMMA/R*np.log(T0/T1))

    h=FL*30.48

    return np.where(h<=11000, \
        P0*np.exp(-g/GAMMA/R*np.log((T0/(T0-GAMMA*h) ))),\
            P11*np.exp(-g/R/T1*(h-11000)) )

当我 运行 我的 M1 Pro 上的代码时,我得到:

In [2]: fl2pres(np.float64([400, 200]))
Out[3]: array([18753.90334892, 46563.239766  ])

和;

In [3]: fl2pres(np.float32([400, 200]))
Out[3]: array([18753.90234375, 46563.25080916])

在我的旧 Intel MacBook Pro 上做同样的事情我得到:

In [2]: fl2pres(np.float64([400, 200]))
Out[2]: array([18753.90334892, 46563.239766  ])

和;

In [3]: fl2pres(np.float32([400, 200]))
Out[3]: array([18753.904296888, 46563.24778944])

float64 计算匹配但 float32 不匹配。我们在代码中大量使用 float32 来优化内存。我知道由于体系结构的差异,可能会发生这种浮点错误,但想知道是否可以进行简单的修复,因为目前某些单元测试失败了。我可以在这些测试中包含架构,但我希望有一个更简单的解决方案吗?

将所有输入转换为 float64 使我的单元测试通过并因此解决了这个问题,但由于我们有相当大的数组和数据帧,对内存的影响是不需要的。

两台笔记本电脑 运行 python 3.9.10 通过自制软件安装,pandas 1.4.1 和 numpy 1.22.3(安装以映射加速和 blas)。

编辑 我更改了函数以打印中间值以查看发生更改的位置:

def fl2pres(FL):
    P0=101325
    T0=288.15
    T1=216.65
    g=9.80665
    R=287.0528742
    GAMMA=0.0065
    P11=P0*np.exp(-g/GAMMA/R*np.log(T0/T1))

    h=FL*30.48
    A = np.log((T0/(T0-GAMMA*h)))
    B = np.exp(-g/GAMMA/R*A)
    C = np.exp(-g/R/T1*(h-11000))
    print(f"P11:{P11}, h:{h}, A:{A}, B:{B}, C:{C}")
    return np.where(h<=11000, P0*B, P11*C)

运行 对于 float32 情况,此函数具有与上面相同的输入,我在 M1 Pro 上:

P11:22632.040591374975, h:[12192.  6096.], A:[0.32161594 0.14793371], B:[0.1844504  0.45954345], C:[0.82864394 2.16691503]
array([18753.90334892, 46563.239766  ])

在英特尔上:

P11:22632.040591374975, h:[12192.  6096.], A:[0.32161596 0.14793368], B:[0.18445034 0.45954353], C:[0.828644 2.166915]
array([18753.90429688, 46563.24778944])

根据我在 numpy 的 GitHub 创建的问题:

the differences you are experiencing seem to be all within a single "ULP" (unit in the last place), maybe 2? For special math functions, like exp, or sin, small errors are unfortunately expected and can be system dependend (both hardware and OS/math libraries).

One thing that could be would might have a slightly larger effect could be use of SVML that NumPy has on newer machines (i.e. only on the intel one). That can be disabled at build time using NPY_DISABLE_SVML=1 as an environment variable, but I don't think you can disable its use without building NumPy. (However, right now, it may well be that the M1 machine is the less precise one, or that they are both roughly the same, just different)

我还没有尝试使用 NPY_DISABLE_SVML=1 编译 numpy,我现在的计划是使用一个 docker 容器,它可以 运行 在我所有的平台上使用一个单一的“真相”用于我的测试。