Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 453
Bathsheba Avatar asked Mar 21 '17 12:03

Bathsheba


People also ask

What is floating-point division by zero?

Floating point error means that there is a division by a zero value in your code. It can be a variable, input, or a function in use that returns zero value. You need to identify this variable/input/function and make sure that the returned value is not zero.

What is the outcome when a floating point number is divided by zero?

Dividing by zero is an operation that has no meaning in ordinary arithmetic and is, therefore, undefined.

What is a floating-point division?

A floating point division where a number divides another number can be expressed as. Thus it can be said that in a floating point division, mantissas are divided and exponents are subtracted. The major steps for a floating point division are. Extract the sign of the result from the two sign bits.

What happens if you divide a nonzero floating point number by zero?

Dividing a floating-point value by zero doesn't throw an exception; it results in positive infinity, negative infinity, or not a number (NaN), according to the rules of IEEE 754 arithmetic.


3 Answers

C++ standard does not force the IEEE 754 standard, because that depends mostly on hardware architecture.

If the hardware/compiler implement correctly the IEEE 754 standard, the division will provide the expected INF, -INF and NaN, otherwise... it depends.

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++ Standard 5.6.4

... If the second operand of / or % is zero the behavior is undefined

C++ Standard 18.3.2.4

...static constexpr bool is_iec559;

...56. True if and only if the type adheres to IEC 559 standard.217

...57. Meaningful for all floating point types.

C++ detection of IEEE754:

The standard library includes a template to detect if IEEE754 is supported or not:

static constexpr bool is_iec559;

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

What if IEEE754 is not supported?

It depends, usually a division by 0 trigger a hardware exception and make the application terminate.

like image 152
Adrian Maire Avatar answered Oct 13 '22 12:10

Adrian Maire


Quoting 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 (see std::numeric_limits::is_iec559), then:

  • if one operand is NaN, the result is NaN

  • dividing a non-zero number by ±0.0 gives the correctly-signed infinity and FE_DIVBYZERO is raised

  • dividing 0.0 by 0.0 gives NaN and FE_INVALID is raised

We are talking about floating-point division here, so it is actually implementation-defined whether double division by zero is undefined.

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.

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.

like image 24
Quentin Avatar answered Oct 13 '22 14:10

Quentin


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.

like image 41
Shafik Yaghmour Avatar answered Oct 13 '22 12:10

Shafik Yaghmour