浮点异常——gcc漏洞?

5

Consider the following code:

#include <fenv.h>
#include <stdio.h>
int main()
{
    #pragma STDC FENV_ACCESS ON
    1.0/0.0;
    printf("%x\n", fetestexcept(FE_ALL_EXCEPT));
}

我希望它打印与FE_DIVBYZERO相对应的非零值,但它却打印0。将main的第二行更改为double x = 1.0/0.0;可以得到预期的行为。这是允许的,还是一个错误? 编辑:值得注意的是,起初看来,在大多数实际代码中,可能会导致fenv异常被引发的操作不能被优化掉,因此可以安全地执行大量计算,并在结束时检查是否发生了任何溢出、除零等情况。然而,当考虑到内联和优化时,事情变得混乱,一个真正的问题出现了。如果这样的函数在始终由于常量参数而最终分母为零的情况下被内联,gcc可能会变得非常聪明,将整个内联函数优化为return INFINITY;而不引发任何异常。

你有使用任何优化标志吗?尝试使用 /o0 并重新测试。 - Ali1S232
根据标准,除以零是未定义的行为。在这种特定情况下,实现是否保证某些行为? - David Thornley
我尝试了 -O0。使用 -O1 或更高版本,添加临时变量也无济于事;程序仍然以任何方式打印 0。 - R.. GitHub STOP HELPING ICE
@David:说得有道理,但IEEE 754规定了行为。我可以尝试使用不同的异常,比如下溢,但我预计会看到相同的情况... - R.. GitHub STOP HELPING ICE
我尝试过的所有编译器(gcc、clang、icc、opencc)似乎都不支持那个编译指示。 - Jens Gustedt
显示剩余2条评论
5个回答

6

接受。顺便说一下,“c99status.html”中的信息相当误导。它称浮点环境支持的缺失为“库问题”,但其余问题不是在库级别,而是在编译器级别。 - R.. GitHub STOP HELPING ICE
即使GCC完全支持C99浮点语义,我认为您也必须关闭FP_CONTRACT。否则,“一个合同表达式也可能省略浮点异常的引发”(6.5/8的脚注)。请注意,在我的快速测试中关闭FP_CONTRACT似乎没有帮助。 - Michael Burr

2
这是一个比较模糊的领域。如果严格按照标准中浮点环境部分的阅读理解,很容易导致认为这是一个错误。然而,我怀疑GCC维护者不会同意这种理解。此外,我也不确定GCC是否声称理解FENV_ACCESS编译指示符。肯定早期版本没有这个功能。

据我所知,当FENV_ACCESS生效时,标准似乎要求将每个浮点运算符视为具有副作用的函数调用...这是否准确? - R.. GitHub STOP HELPING ICE
1
@R..:不是“完全”,但几乎是。特别是,“好像”规则正在生效;编译器仍然可以执行任何不改变序列点处可观察行为的优化。值得注意的是,标准明确要求不支持FENV_ACCESS的编译器实现“ON”行为。这是GCC中的一个错误。 - Stephen Canon
我在7.6的标准里没有找到任何比6.5.5/5更重要的规定,这导致了除以零的未定义行为。Annex F似乎只适用于当实现定义了__STDC_IEC_559__时,而我不知道gcc的任何版本是否如此定义。我可能忽略了某些东西,但如果没有,程序将展示未定义行为。有趣的是尝试使用一个独立的表达式来引起浮点行为,而不是除以零(可能会溢出),这样6.5.5/5就绝对不适用。 - David Thornley

2

使用gcc 4.6.0编译时,使用-Wall参数会显示以下信息:

f.c:5:0: warning: ignoring #pragma FENV_ACCESS ON [-Wunknown-pragmas]

根据GCC信息页面的说法:
* `The default state for the `FENV_ACCESS' pragma (C99 7.6.1).'

 This pragma is not implemented, but the default is to "off" unless
 `-frounding-math' is used in which case it is "on".

很遗憾,-frounding-math 对你的程序似乎没有任何影响。

可以说这是编译器的一个错误;我建议在GCC邮件列表中咨询一下。


0

你的编译器可能已经将原始版本优化掉了。认识到这两个常量在任何非平凡意义下都不被“使用”,它甚至可能不存在于编译后的二进制文件中。

第二个例子通过实际将操作赋值给一个变量来改变这种情况。


由于 #pragma FE_ENV_ACCESS ON,该语句具有副作用,因此除非标准中有一些特殊的例外允许它被优化掉,否则无法进行优化。 - R.. GitHub STOP HELPING ICE

0

我认为在第一种情况下,表达式被优化掉了,但在第二种情况下没有。我可以使用gcc 4.2和gcc -O0重现您的结果,但是如果我切换到gcc -O3,那么两种情况下都会得到0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接