>我正在调试实现算法的代码,该算法的主循环在语句为真时终止 à las >= u || s <= l
,其中s
、u
和l
double
在主循环中更新。在此示例中,所有三个变量始终介于0.5
和1.5
之间。我在这里不包括代码,因为它不是我写的,提取 MWE 很难。我对代码在不同架构上的行为不同感到困惑,我希望下面的线索可以帮助我缩小算法中的错误范围。
一些浮点舍入似乎是该错误的根本原因。以下是我到目前为止所确定的:
- 该算法在 x86-64 上的所有优化级别上正确终止。
- 该算法在 arm64、mips64 和 ppc64 上以 -O3(未尝试其他选择级别(正确终止。
- 该算法在 i686 上以 -O0 正确终止。
- 该算法在 i686 上无限期地循环使用 -O1、-O2 和 -O3。
-
主要问题点:在算法无限循环的情况下,如果在与
l
和u
进行比较之前打印s
(std::cout << s << std::endl
(,则可以使其正确终止。
什么样的编译器优化可能与这里相关?
上述所有行为都是在GNU/Linux系统上观察到的,并在GCC 6.4、7.3和8.1中重现。
由于您说您的代码在 x86-64 和其他指令集上按预期工作,但在 i686 上中断,但仅在一些优化级别下中断,因此可能的罪魁祸首是 x86 扩展精度。
在 x86 上,浮点指令将结果存储在寄存器中,其精度高于随后将这些值存储在内存中时。因此,当编译器可以重用寄存器中已加载的相同值时,与必须保存并重新加载值时相比,结果可能会有所不同。打印值可能需要保存并重新加载它。
这是GCC中一个众所周知的非错误。
GCC 提供了一个-ffloat-store
命令行选项,可能会有所帮助:
-ffloat-store
不要在寄存器中存储浮点变量,并禁止其他选项,这些选项可能会更改浮点值是从寄存器还是存储器中获取的。
此选项可防止在诸如 68000 之类的机器上出现不希望的过分精度,其中浮动寄存器(68881 的(保持比双精度更高的精度。x86 体系结构也是如此。对于大多数程序来说,过多的精度只会有好处,但少数程序依赖于IEEE浮点的精确定义。在修改此类程序以将所有相关的中间计算存储到变量中后,对此类程序使用
-ffloat-store
。
正如那里提到的,它不会自动让你的代码像在其他指令集上一样工作。您可能需要修改代码以将结果显式存储在变量中。