Floating point x86_64和ARMv8.2-A之间的浮点计算结果不同

Floating point x86_64和ARMv8.2-A之间的浮点计算结果不同,floating-point,fortran,arm,precision,Floating Point,Fortran,Arm,Precision,我在aarch64和x86_64中编译了相同的Fortran库和代码。它是一个跨n维数组/矩阵运行算法的模型。ARM的CPU是亚马逊重力仪2。在为x86_64编译和运行代码时,AWS中的AMD和Intel选项会产生相同的结果 我正在使用带有以下标志的gcc/g++/gfortran/mpich(所有版本均为8.3.0,来自debian buster的主repos) 这一切编译和运行都很好,但是,我注意到在模型的输出中,结果略有不同。这似乎是精度或舍入的问题,因为输出之间的大多数值是相同的。但是,

我在aarch64和x86_64中编译了相同的Fortran库和代码。它是一个跨n维数组/矩阵运行算法的模型。ARM的CPU是亚马逊重力仪2。在为x86_64编译和运行代码时,AWS中的AMD和Intel选项会产生相同的结果

我正在使用带有以下标志的gcc/g++/gfortran/mpich(所有版本均为8.3.0,来自debian buster的主repos)

这一切编译和运行都很好,但是,我注意到在模型的输出中,结果略有不同。这似乎是精度或舍入的问题,因为输出之间的大多数值是相同的。但是,在整个输出中都有(似乎)随机值,其中一个拱的编译代码要么向下舍入,要么被截断,而另一个拱的编译代码向上舍入

输出存储为NetCDF(使用NetCDF Fortran版本4.5.3),文件的md5sum在x86_64 CPU上相同,但在aarch64上不同

你知道为什么会这样吗?或者我可以在编译过程中使用任何标志来确保在不同的体系结构中得到相同的结果


我现在看到的值的精度是小数点后5位,即123.12345

这里是一个来自输出的
diff
的片段,您可以看到大多数值是相同的,但一些值的舍入方式似乎不同(我用**标记了不同的值):

657c657
<     18.83633, 18.83212, 18.82778, **18.82337**, 18.81886, 18.81425, 18.80956, 
---
>     18.83633, 18.83212, 18.82778, **18.82336**, 18.81886, 18.81425, 18.80956, 
1151c1151
<     17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.4478**, 17.46622, 
---
>     17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.44779**, 17.46622, 
1711c1711
<     19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77241**, 
---
>     19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77242**, 
2130c2130
<     20.06532, 20.06839, **20.07135**, 20.07423, 20.07702, 20.0797, 20.0823, 
---
>     20.06532, 20.06839, **20.07136**, 20.07423, 20.07702, 20.0797, 20.0823, 
2140c2140
<     20.04788, 20.04424, 20.04047, **20.03661**, 20.03268, 20.02863, 20.02448, 
---
>     20.04788, 20.04424, 20.04047, **20.03662**, 20.03268, 20.02863, 20.02448, 
2600c2600
<     11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72186**, 11.75784, 
---
>     11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72185**, 11.75784,

如果代码仅使用基本的算术运算,如+、-、*、/和sqrt,并且编译器处于IEEE754一致性模式,则无论使用何种CPU,输出都应该是位相同的。 此IEEE754一致性模式通常是默认设置

否则,问题可能是由编译器或CPU错误引起的

诸如
-ffast math
之类的选项将编译器置于非IEEE 754一致性模式。 然后,它使用数学等价规则来优化代码,这些代码不一定是数值等价的(例如,
((a*a)*a)*a->(a*a)*(a*a)
等等)。 如果是这种情况,并且编译器对ARM代码的优化与x86_64不同,这可能是一种解释

此外,如果代码使用诸如
sin
cos
exp
atan2
之类的函数,则如果使用完全相同的运行时库,则输出将只有一点相同。这是因为这些函数没有正确地四舍五入,结果通常有一个微小的误差(这可能会在计算中放大,并以你观察它的方式显示出来)。 对于x86_64,也可能使用用于这些功能的特殊CPU指令,对于ARM a软件实现,也可能使用特殊CPU指令,反之亦然。请注意,即使这些函数在CPU/FPU上实现,它们也没有正确舍入,很可能使用了不同的算法

TL/DR:检查
-ffast math
的编译器标志,或尝试在选项末尾添加
-fno fast math


编辑:正如@Rob在评论中提到的,可以添加另一个标志
-ffp contract=off
。在gcc中,默认情况下为“快速”(独立于
-ffast math
),即使未明确请求,也可能生成FMA指令。这也破坏了754的一致性。

“小数点后5位的精度,即123.12345”对于浮点数,最好表示为精度的小数点后8位。更好的是24位二进制精度。关于人们对它的期望。@Marty:浮点不是我的专业领域,因此我觉得我无法提供答案。我只想说,即使x86_64和ARMv8-a实现了相同的标准,浮点计算结果可能会有所不同-请参阅,或者编译器在某些与体系结构相关的方式上可能会有不同的行为:您可能需要仔细检查是否存在任何特定于体系结构的舍入/显示选项。这是浮点(单精度、32位、4字节)的expexted precisiin。CPU不同,程序集也不同。优化可能会有所不同。什么样的操作特别不同?他们的源代码是什么?如何编译代码?哪些标志?还要注意,那些相同的标志可能只是在其他小数位上有所不同(您不打印)。@Marty:如果用硬件生成的十六进制来显示二进制值,而不是十进制值,并再次执行比较,这将是很有趣的。不过,我不确定用Fortran和C相比这有多容易。你忘了OP是在输出数据,所以有IO例程可以将二进制转换成十进制。IEEE-754讨论了转换,但尚不清楚OP是否有严格符合IEEE-754 IO库。此外,代码是否是线程或多进程的,如r
-O2 -ftree-vectorize -funroll-loops -w -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4
  657c657
  <     18.83633, 18.83212, 18.82778, **18.82337**, 18.81886, 18.81425, 18.80956, 
  ---
  >     18.83633, 18.83212, 18.82778, **18.82336**, 18.81886, 18.81425, 18.80956, 
  1151c1151
  <     17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.4478**, 17.46622, 
  ---
  >     17.35448, 17.37331, 17.39206, 17.41071, 17.42931, **17.44779**, 17.46622, 
  1711c1711
  <     19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77241**, 
  ---
  >     19.77562, 19.77532, 19.77493, 19.77445, 19.77386, 19.77319, **19.77242**, 
  2130c2130
  <     20.06532, 20.06839, **20.07135**, 20.07423, 20.07702, 20.0797, 20.0823, 
  ---
  >     20.06532, 20.06839, **20.07136**, 20.07423, 20.07702, 20.0797, 20.0823, 
  2140c2140
  <     20.04788, 20.04424, 20.04047, **20.03661**, 20.03268, 20.02863, 20.02448, 
  ---
  >     20.04788, 20.04424, 20.04047, **20.03662**, 20.03268, 20.02863, 20.02448, 
  2600c2600
  <     11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72186**, 11.75784, 
  ---
  >     11.54104, 11.57732, 11.61352, 11.6497, 11.68579, **11.72185**, 11.75784,