浮点除以z的行为

考虑

#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.    
}

我一直认为a将为+ Inf,b将为-Inf,c将为NaN。 但是我也听到谣言,严格说来,浮点数除以零的行为是不确定的,因此上述代码不能视为可移植的C ++。 (从理论上讲,这消除了我的百万行代码堆栈的完整性。糟糕。)

谁是对的?

注意,我对定义的实现感到满意,但是我在这里谈论的是吃东西,恶魔打喷嚏的未定义行为。

7个解决方案
40 votes

C ++标准不强制执行IEEE 754标准,因为这主要取决于硬件体系结构。

如果硬件/编译器正确实施了IEEE 754标准,则该部门将提供预期的INF,-INF和NaN,否则...取决于。

未定义的方法由编译器实现决定,并且有很多变量,例如硬件体系结构,代码生成效率,编译器开发人员的懒惰等。

资源:

C ++标准规定0.0除以undefined

C ++标准5.6.4

...如果/或%的第二个操作数为零,则行为不确定

C ++标准18.3.2.4

...静态constexpr bool is_iec559;

... 56。 当且仅当类型符合IEC 559标准时才为217。

... 57。 对所有浮点类型有意义。

IEEE754的C ++检测:

标准库包括一个模板,用于检测是否支持IEEE754:

静态constexpr bool is_iec559;

#include <numeric>
bool isFloatIeee754 = std::numeric_limits<float>::is_iec559();

如果不支持IEEE754怎么办?

通常,将其除以0会触发硬件异常并使应用程序终止。

Adrian Maire answered 2020-02-13T18:47:10Z
23 votes

引用cppreference:

如果第二个操作数为零,则行为是不确定的,除了如果进行浮点除法并且该类型支持IEEE浮点算术(请参见FE_DIVBYZERO),则:

  • 如果一个操作数为NaN,则结果为NaN

  • 将非零数字除以±0.0得到正确签名的无穷大,并引发FE_DIVBYZERO

  • 用0.0除以0.0得到NaN并引发true

我们在这里讨论的是浮点除法,因此实际上是否由true除以零是不确定的,这实际上是实现定义的。

如果truetrue,并且它是“通常是true”,则该行为是明确定义的,并会产生预期的结果。

一个比较安全的选择是:

static_assert(std::numeric_limits<double>::is_iec559, "Please use IEEE754, you weirdo");

...靠近您的代码。

Quentin answered 2020-02-13T18:48:06Z
8 votes

除以0是未定义的行为。

根据C ++标准(C ++ 11)第5.6节:

二进制/运算符产生商,而二进制%运算符   产生第一个表达式除以的余数   第二。 如果/%的第二个操作数为零,则行为为   未定义。 对于整数操作数/运算符可得出代数   除掉任何小数部分的商; 如果商a/b是   结果类型中可表示的(a/b)*b + a%b等于a

/运算符的整数和浮点操作数之间没有区别。 该标准仅规定了不考虑除以零而不考虑操作数。

dbush answered 2020-02-13T18:48:40Z
7 votes

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

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

尽管实现可以可选地支持附件F,该附件F具有明确定义的语义,用于将浮点数除以零。

从此clang错误报告中我们可以看到clang消毒程序将IEC 60559浮点数除以零定义为未定义,即使定义了宏__STDC_IEC_559__,它仍由系统头定义,并且至少对于clang不支持附件F等等。 对于clang仍然是未定义的行为:

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

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

#if STDC_IEC_559

这个宏是由您的系统标题而不是我们定义的; 这是   系统标题中的错误。 (FWIW,海湾合作委员会不完全支持附件   还是IIRC,所以它甚至不是Clang特有的错误。)

该错误报告和其他两个错误报告UBSan:浮点除以零不是没有定义,并且clang应该支持ISO C(IEC 60559 / IEEE 754)的附件F,表明gcc在浮点除以零方面符合附件F。 。

尽管我同意无条件地定义STDC_IEC_559并非由C库决定,但问题仅针对clang。 GCC并不完全支持Annex F,但是至少其意图是默认支持它,并且如果不改变舍入模式,则可以很好地定义部门。 如今,不支持IEEE 754(至少是基本功能,如零除处理)被视为不良行为。

GCC Wiki中的gcc浮点数学语义进一步支持了这一点,该语义表明-fno-signaling-nans是默认值,与gcc优化选项文档一致,该文档显示:

缺省值为-fno-signaling-nans。

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

检测浮点除以零。 与其他类似选项不同,-fsanitize = undefined不会启用-fsanitize = float-divide-by-zero(零除数),这是因为浮点除以零可能是获得无限和NaN的合法方法。

看到它为clang直播,为gcc直播。

Shafik Yaghmour answered 2020-02-13T18:45:44Z
6 votes

在[expr] / 4中,

如果在表达式的求值过程中,未在数学上定义结果或该类型的结果不在可表示值的范围内,则行为不确定。 [注意:大多数现有的C ++实现都忽略整数溢出。 除以零的处理,使用零除数形成余数,并且所有浮点异常在机器之间都不同,并且通常可以通过库函数进行调整。 —尾注]

重点矿

因此,按照标准,这是未定义的行为。 确实确实说,其中一些情况实际上是由实现处理的并且是可配置的。 因此,它不会说它是实现定义的,但是它确实让您知道实现确实定义了这种行为。

NathanOliver answered 2020-02-13T18:49:15Z
1 votes

关于提交人的问题“谁是正确的?”,可以肯定地说两个答案都是正确的。 C标准将行为描述为“未定义”这一事实并不能说明底层硬件的实际功能。 这只是意味着,如果您希望您的程序按照标准有意义,那么-您可能不会假设-硬件实际上实现了该操作。 但是,如果碰巧在实现IEEE标准的硬件上运行,则会发现该操作实际上已实现,其结果符合IEEE标准的规定。

PMar answered 2020-02-13T18:49:36Z
0 votes

这也取决于浮点环境。

cppreference具有详细信息:[http://en.cppreference.com/w/cpp/numeric/fenv](虽然没有示例)。

在大多数台式机/服务器C ++ 11和C99环境中应该可用。 在所有这些标准化之前,还存在特定于平台的变体。

我希望启用异常会使代码运行更慢,所以可能由于这个原因,我知道的大多数默认情况下都禁用异常的平台。

Paul Floyd answered 2020-02-13T18:50:09Z
translate from https://stackoverflow.com:/questions/42926763/the-behaviour-of-floating-point-division-by-zero