浮点除以零的行为

考虑

#include <iostream>主函数(){双a = 1.0/0;双 b = -1.0/0;双 c = 0.0/0;std::cout <<一个<<b <

我一直以为a会是+Inf,b会是-Inf,c会是NaN.但我也听说过严格来说浮点除以零的行为是undefined,因此上面的代码不能被认为是可移植的C++.(理论上这会抹杀我的百万行代码堆栈的完整性.哎呀.)

谁是对的?

注意我对定义的实现很满意,但我在这里谈论的是吃猫、打喷嚏未定义的行为.

解决方案

除以零整数和浮点都是未定义的行为 [expr.mul]p4:

<块引用>

二元/运算符产生商,二元 % 运算符产生除法的余数第二个表达式的第一个表达式.如果/或 % 的第二个操作数为零,则行为未定义. ...

虽然实现可以选择支持 附件 F浮点除以零的语义.

我们可以从这个 clang 错误报告中看到 clang sanitizer 关于 IEC 60559 浮点除以零为未定义,即使宏 __STDC_IEC_559__ 已定义,它仍由系统头文件定义,至少对于 clang 不支持 Annex F所以对于 clang 仍然是未定义的行为:

<块引用><块引用>

C 标准的附录 F(IEC 60559/IEEE 754 支持)定义了浮点除以零,但 clang(3.3 和 3.4 Debian 快照)认为它是未定义的.这是不正确的:

对附件 F 的支持是可选的,我们不支持.

<块引用>

#if STDC_IEC_559

这个宏是由您的系统头文件定义的,而不是由我们定义的;这是系统标头中的错误.(FWIW、GCC 不完全支持附件F 要么,IIRC,所以它甚至不是 Clang 特定的错误.)

该错误报告和其他两个错误报告 UBSan:浮点除以零不是未定义的 和 clang 应该支持 ISO C 的 Annex F (IEC 60559/IEEE 754) 表示 gcc 在浮点除以零方面符合 附录 F.

<块引用>

虽然我同意 C 库不能无条件地定义 STDC_IEC_559,但问题是特定于 clang 的.GCC 并不完全支持 Annex F,但至少它的意图是在默认情况下支持它,并且如果舍入模式没有改变,那么除法已经很好地定义了. 现在不支持 IEEE 754 (至少像处理除以零这样的基本功能)被视为不良行为.

这是 gcc Semantics of Floating Point Math in GCC wiki 的进一步支持,这表明-fno-signaling-nans 是默认值这与 gcc 优化选项文档 一致,其中说:

<块引用>

默认值为 -fno-signaling-nans.

有趣的是,UBSan 默认包含 float-divide-fsanitize=undefined 下的 -by-zero 而 gcc 没有:

<块引用>

检测浮点除以零.与其他类似选项不同,-fsanitize=undefined 不启用 -fsanitize=float-divide-by-zero,因为浮点除以零可能是获得无穷大和 NaN 的合法方式.

查看 为 clang 直播 和 为 gcc 直播.

Consider

#include <iostream>
int main()
{
    double a = 1.0 / 0;
    double b = -1.0 / 0;
    double c = 0.0 / 0;
    std::cout << a << b << c; // to stop compilers from optimising out the code.    
}

I have always thought that a will be +Inf, b will be -Inf, and c will be NaN. But I also hear rumours that strictly speaking the behaviour of floating point division by zero is undefined and therefore the above code cannot considered to be portable C++. (That theoretically obliterates the integrity of my million line plus code stack. Oops.)

Who's correct?

Note I'm happy with implementation defined, but I'm talking about cat-eating, demon-sneezing undefined behaviour here.

解决方案

Division by zero both integer and floating point are undefined behavior [expr.mul]p4:

The binary / operator yields the quotient, and the binary % operator yields the remainder from the division of the first expression by the second. If the second operand of / or % is zero the behavior is undefined. ...

Although implementation can optionally support Annex F which has well defined semantics for floating point division by zero.

We can see from this clang bug report clang sanitizer regards IEC 60559 floating-point division by zero as undefined that even though the macro __STDC_IEC_559__ is defined, it is being defined by the system headers and at least for clang does not support Annex F and so for clang remains undefined behavior:

Annex F of the C standard (IEC 60559 / IEEE 754 support) defines the floating-point division by zero, but clang (3.3 and 3.4 Debian snapshot) regards it as undefined. This is incorrect:

Support for Annex F is optional, and we do not support it.

#if STDC_IEC_559

This macro is being defined by your system headers, not by us; this is a bug in your system headers. (FWIW, GCC does not fully support Annex F either, IIRC, so it's not even a Clang-specific bug.)

That bug report and two other bug reports UBSan: Floating point division by zero is not undefined and clang should support Annex F of ISO C (IEC 60559 / IEEE 754) indicate that gcc is conforming to Annex F with respect to floating point divide by zero.

Though I agree that it isn't up to the C library to define STDC_IEC_559 unconditionally, the problem is specific to clang. GCC does not fully support Annex F, but at least its intent is to support it by default and the division is well-defined with it if the rounding mode isn't changed. Nowadays not supporting IEEE 754 (at least the basic features like the handling of division by zero) is regarded as bad behavior.

This is further support by the gcc Semantics of Floating Point Math in GCC wiki which indicates that -fno-signaling-nans is the default which agrees with the gcc optimizations options documentation which says:

The default is -fno-signaling-nans.

Interesting to note that UBSan for clang defaults to including float-divide-by-zero under -fsanitize=undefined while gcc does not:

Detect floating-point division by zero. Unlike other similar options, -fsanitize=float-divide-by-zero is not enabled by -fsanitize=undefined, since floating-point division by zero can be a legitimate way of obtaining infinities and NaNs.

See it live for clang and live for gcc.

相关文章