gfortran 对 cos(atan(x)) 执行不安全的数学优化

问题描述 投票:0回答:1

我正在研究一个问题,即使使用编译选项 -fno-fast-math 和 -fno-unsafe-math-optimizations,gfortran 在极少数情况下也会根据优化级别返回不同的结果,因此结果不应依赖于优化级别。我使用 gfortran-11.2.1、gfortran-11.4.1(均为 RedHat)和 gfortran-14.2(godbolt)对此进行了测试。为了获得可重现的结果,我还需要使用 OpenLibM 数学库而不是 gcc 数学库,因此我将继续讨论 godbolt 反汇编。

结果差异来自计算 y = cos(atan(x)),对于 -O1 或更高版本,它被替换为 y = 1/sqrt(1+x^2):编译

double precision function foo(x)
implicit none
double precision, intent(in) :: x
foo = cos(atan(x))
end function foo

使用

gfortran -c file.f90 -O1
,请参阅https://godbolt.org/z/c9b4rvhdW并检查创建的代码:没有调用cos或atan,而是使用sqrt。如果使用
-O0
,则使用 atan 和 cos 函数。

从文档中我希望

-fno-fast-math
-fno-unsafe-math-optimizations
应该禁用此优化,但看起来这些对 gfortran 没有影响。只有
-fno-tree-forwprop
禁用此优化,以便返回与 -O0 相同的结果。

对于 gcc C 编译器的行为符合预期:对于相同代码的 C 变体 (https://godbolt.org/z/E7Mzh6Taq),此优化仅由编译器在指定

-ffast-math
时完成,但默认情况下不这样做
 -O1

使用

-O0
或 '-fno-tree-forwprop` 是禁用这些数学优化的唯一选项吗?我希望确保所有优化级别都获得相同的结果,但保持尽可能好的性能。

fortran compiler-optimization gfortran
1个回答
0
投票

准确性不重要是一件好事。 在区间 [0:137] 中测试 1000 万个

x
值表明,如果阻止优化,则 8984174 (89.84%) 的
cos(atan(x))
结果的 ULP 超过 2 ULP。 观察到的最大 ULP 为 129.42,相当于 5 个错误的尾随数字。 如果计数阈值增加到 100 ULP,则会发现 144035 (1.44%) 个值超过 100 ULP。

经过优化,只有 26 个结果超过 2。

如上所述,无需使用编译器即可阻止优化。

foo()
可以写成

double precision function foo(x)
   implicit none
   double precision, intent(in) :: x
   double precision, volatile :: tmp
   tmp = atan(x)
   foo = cos(tmp)
end function foo

volatile
属性告诉编译器,在计算
tmp
和在
atan(x)
中引用
tmp
之间,
cos(tmp)
的值可能会通过编译器无法控制的某种方式发生变化。 编译器将从内存中重新加载 tmp。

© www.soinside.com 2019 - 2024. All rights reserved.