简体   繁体   English

浮点数除以零的行为

[英]The behaviour of floating point division by zero

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.我一直认为a会是+Inf, b会是-Inf, c会是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++.但我也听到谣言说,严格来说浮点除以零的行为是未定义的,因此上面的代码不能被认为是可移植的 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.注意我对实现定义感到满意,但我在这里谈论的是吃猫、打喷嚏的未定义行为

C++ standard does not force the IEEE 754 standard, because that depends mostly on hardware architecture. C++ 标准不强制使用 IEEE 754 标准,因为这主要取决于硬件架构。

If the hardware/compiler implement correctly the IEEE 754 standard, the division will provide the expected INF, -INF and NaN, otherwise... it depends.如果硬件/编译器正确实现了 IEEE 754 标准,则该部门将提供预期的 INF、-INF 和 NaN,否则……这取决于。

Undefined means, the compiler implementation decides, and there are many variables to that like the hardware architecture, code generation efficiency, compiler developer laziness, etc..未定义意味着,编译器实现决定,并且有很多变量,比如硬件架构、代码生成效率、编译器开发人员的懒惰等。

Source:来源:

The C++ standard state that a division by 0.0 is undefined C++ 标准规定除以 0.0 是undefined

C++ Standard 5.6.4 C++ 标准 5.6.4

... If the second operand of / or % is zero the behavior is undefined ... 如果 / 或 % 的第二个操作数为零,则行为未定义

C++ Standard 18.3.2.4 C++ 标准 18.3.2.4

...static constexpr bool is_iec559; ...static constexpr bool is_iec559;

...56. ...56。 True if and only if the type adheres to IEC 559 standard.217当且仅当类型符合 IEC 559 标准 217 时为真

...57. ...57。 Meaningful for all floating point types.对所有浮点类型都有意义。

C++ detection of IEEE754: IEEE754的C++检测:

The standard library includes a template to detect if IEEE754 is supported or not:标准库包含一个模板来检测是否支持 IEEE754:

static constexpr bool is_iec559;静态 constexpr bool is_iec559;

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

What if IEEE754 is not supported?如果不支持 IEEE754 怎么办?

It depends, usually a division by 0 trigger a hardware exception and make the application terminate.这取决于,通常除以 0 会触发硬件异常并使应用程序终止。

Quoting cppreference :引用cppreference

If the second operand is zero, the behavior is undefined, except that if floating-point division is taking place and the type supports IEEE floating-point arithmetic (seestd::numeric_limits::is_iec559 ), then:若第二个操作数为零,则行为未定义,除非发生浮点除法且类型支持 IEEE 浮点算术(见std::numeric_limits::is_iec559 ),则:

  • if one operand is NaN, the result is NaN如果一个操作数为 NaN,则结果为 NaN

  • dividing a non-zero number by ±0.0 gives the correctly-signed infinity and FE_DIVBYZERO is raised将非零数除以 ±0.0 给出正确签名的无穷大,并引发FE_DIVBYZERO

  • dividing 0.0 by 0.0 gives NaN and FE_INVALID is raised 0.0 除以 0.0 给出 NaN 并FE_INVALID

We are talking about floating-point division here, so it is actually implementation-defined whether double division by zero is undefined.我们这里讨论的是浮点除法,所以实际上是实现定义的,是否被零double除是未定义的。

If std::numeric_limits<double>::is_iec559 is true , and it is"usually true " , then the behaviour is well-defined and produces the expected results.如果std::numeric_limits<double>::is_iec559true ,并且“通常为true ,则行为定义良好并产生预期结果。

A pretty safe bet would be to plop down a:一个相当安全的赌注是放下一个:

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

... near your code. ...靠近您的代码。

Division by zero both integer and floating point are undefined behavior [expr.mul]p4 :除以零整数和浮点数是未定义的行为[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.尽管实现可以选择性地支持附件 F ,它具有明确定义的浮点除以零的语义。

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:我们可以从这个 clang 错误报告中看到, clang sanitizer 将 IEC 60559 浮点除以零视为未定义,即使定义了宏__STDC_IEC_559__ ,它也是由系统头定义的,至少对于 clang 不支持Annex F等因为 clang 仍然是未定义的行为:

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. C 标准的附件 F(IEC 60559 / IEEE 754 支持)定义了浮点除以零,但 clang(3.3 和 3.4 Debian 快照)将其视为未定义。 This is incorrect:这是不正确的:

Support for Annex F is optional, and we do not support it.对附件 F 的支持是可选的,我们不支持。

#if STDC_IEC_559 #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.) (FWIW,GCC 也不完全支持 Annex F,IIRC,所以它甚至不是 Clang 特定的错误。)

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.该错误报告和另外两个错误报告UBSan:浮点除以零不是未定义的clang 应该支持 ISO C(IEC 60559 / IEEE 754)的附件 F表明 gcc 在浮点除以零方面符合附件 F .

Though I agree that it isn't up to the C library to define STDC_IEC_559 unconditionally, the problem is specific to clang.尽管我同意 C 库不能无条件地定义STDC_IEC_559 ,但问题是特定于 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. GCC 并不完全支持 Annex F,但至少它的意图是默认支持它,并且如果舍入模式没有改变,则划分是明确定义的。 Nowadays not supporting IEEE 754 (at least the basic features like the handling of division by zero) is regarded as bad behavior.如今,不支持 IEEE 754(至少是处理除零等基本功能)被视为不良行为。

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:这是GCC wiki 中浮点数学的 gcc Semantics 的进一步支持,它表明-fno-signaling-nans是默认值,这与gcc 优化选项文档一致,其中说:

The default is -fno-signaling-nans.默认值为 -fno-signaling-nans。

Interesting to note that UBSan for clang defaults to including float-divide-by-zero under -fsanitize=undefined while gcc does not :有趣的是, UBSan for clang 默认在-fsanitize=undefined下包含浮点除以零,而gcc 没有

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.与其他类似选项不同, -fsanitize=float-divide-by-zero 不能通过 -fsanitize=undefined 启用,因为浮点数除以零可能是获得无穷大和 NaN 的合法方法。

See it live for clang and live for gcc .在 clanggcc 中查看它。

Division by 0 is undefined behavior .除以 0 是未定义的行为

From section 5.6 of the C++ standard (C++11) :来自C++ 标准 (C++11) 的第 5.6 节:

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.如果/%的第二个操作数为零,则行为未定义。 For integral operands the / operator yields the algebraic quotient with any fractional part discarded;对于整数操作数, /运算符产生代数商,其中任何小数部分都被丢弃; if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a .如果商a/b可在结果类型中表示,则(a/b)*b + a%b等于a

No distinction is made between integer and floating point operands for the / operator. /运算符的整数和浮点操作数之间没有区别。 The standard only states that dividing by zero is undefined without regard to the operands.该标准仅说明除以零是未定义的,而不考虑操作数。

In [expr]/4 we have在 [expr]/4 我们有

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined .如果在对表达式求值期间,结果未在数学上定义或不在其类型的可表示值范围内,则行为为 undefined [ Note: most existing implementations of C++ ignore integer overflows. [ 注意:大多数现有的 C++ 实现都忽略整数溢出。 Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function.除以零的处理,使用零除数形成余数,以及所有浮点异常在机器之间有所不同,通常可以通过库函数进行调整。 —end note ] ——尾注]

Emphasis mine强调我的

So per the standard this is undefined behavior.所以根据标准,这是未定义的行为。 It does go on to say that some of these cases are actually handled by the implementation and are configurable.它确实继续说,其中一些情况实际上是由实现处理的并且是可配置的。 So it won't say it is implementation defined but it does let you know that implementations do define some of this behavior.所以它不会说它是实现定义的,但它确实让你知道实现确实定义了一些这种行为。

As to the submitter's question 'Who's correct?', it is perfectly OK to say that both answers are correct.至于提交者的问题“谁是正确的?”,可以说两个答案都是正确的。 The fact that the C standard describes the behavior as 'undefined' DOES NOT dictate what the underlying hardware actually does; C 标准将行为描述为“未定义”这一事实并没有规定底层硬件实际做什么; it merely means that if you want your program to be meaningful according to the standard you -may not assume- that the hardware actually implements that operation.它仅仅意味着,如果您希望您的程序根据标准有意义,您 - 可能不会假设 - 硬件实际上实现了该操作。 But if you happen to be running on hardware that implements the IEEE standard, you will find the operation is in fact implemented, with the results as stipulated by the IEEE standard.但是如果你碰巧运行在实现了IEEE标准的硬件上,你会发现操作实际上是实现的,结果是IEEE标准规定的。

This also depends on the floating point environment.这也取决于浮点环境。

cppreference has details: http://en.cppreference.com/w/cpp/numeric/fenv (no examples though). cppreference 有详细信息: http : //en.cppreference.com/w/cpp/numeric/fenv (虽然没有示例)。

This should be available in most desktop/server C++11 and C99 environments.这应该在大多数桌面/服务器 C++11 和 C99 环境中可用。 There are also platform-specific variations that predate the standardization of all this.在所有这些标准化之前,还有特定于平台的变体。

I would expect that enabling exceptions makes the code run more slowly, so probably for this reason most platforms that I know of disable exceptions by default.我希望启用异常会使代码运行得更慢,因此可能出于这个原因,我所知道的大多数平台默认禁用异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM