Reputation: 234855
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.
Upvotes: 66
Views: 36658
Reputation: 81267
The issue here is that IEEE-754 specifies behaivor in many cases for which some implementations may not be able to guarantee behavior consistent with sequential program execution, and which the Standard consequently categorizes aas Undefined Behavior.
Neither the C89 Standard, nor any C or C++ Standard that has been derived from it, has attempted to systematically distinguish between statements with the meanings:
Even though many implementations' specifications would specify their behavior in cases including X, and many readers may be unaware of any implementations that don't, readers should be aware that not all implementations specify their behavior in that case. Under this interpretation, the IEEE-754 behavioral specification would dominate.
Implementations that would specify their behavior in cases including X, may deviate from their specifications in case X if there is a compelling reason for doing so that is either obvious or documented; people designing implementations should be expected to exercise reasonable judgment as to what reasons are compelling and/or obvious. Under this interpretation, the IEEE-754 spec would generally dominate, but an implementation might e.g. allow floating-point traps to be enabled while also hoisting or deferring loop-invariant floating-point calculations in ways that would cause a divide-by-zero trap to occur at a time which is observably inconsistent with any possible sequence in which program actions might be performed.
Even if implementations would (by spec) guarantee behavior in cases including X, and even if it would be useful for them to behave that way in case X, any program that would rely upon their actually behaving that way should be considered erroneous. Under this interpretation, a statement that some action has undefined behavior implies that the action should always be considered erroneous, even if the Standard or other documentation would otherwise specify the behavior thereof.
Note that both of the first two interpretations uphold the Principle of Least Astonishment, but not all compiler writers subscribe to that philosophy. Some compiler writers prefer the third interpretation, and as since the Standard waives judgment as to what behavioral definitions have compelling and obvious benefits, such an interpretation is perfectly valid as far as the Standards are concerned.
Upvotes: 0
Reputation: 5537
The spec passage saying that division-by-zero is UB was never true nor helpful; this was merely a defect in the C++11 spec, which has since been (mostly) fixed, so we can stop worrying about it and arguing about it; the cats are safe.
I haven't purchased a recent official C++ spec, but cppreference summarizes in:
If the second operand is zero, the behavior is undefined, except that if the IEEE floating-point arithmetic is supported, and the floating-point division is taking place, then [... the IEEE754 behavior we all know ...].
std::numeric_limits<T>::is_iec559
says that IEEE754 usually is supported, for types float
, double
, and long double
, and describes how a program can test whether it is (at compile-time-- we can tell it's at compile-time since the test function is constexpr).In other words, paraphrasing (and strengthening "usually" to "almost always" to reflect reality; I've never encountered a c++ compiler that didn't support it):
or equivalently:
I, and many thousands of other developers, heavily depend on IEEE754 behavior behavior/semantics in which 0/0 is NaN, 1/0 is +infinity, etc., in billions of lines of existing c++ code which runs the world. We have since the beginning of time, even back in C++11 when the spec unhelpfully called it UB.
Real-world UB means (more literally than cat-eating and demon-sneezing), typically, when the compiler randomly/uncontrolledly rewrites the program and the compiler team doesn't apologize and laughs at you instead; that would certainly be a cause for alarm and worry, but floating-point divide-by-zero has never been in that category.
Upvotes: 0
Reputation: 158599
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.
Upvotes: 16
Reputation: 14865
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.
Upvotes: 48
Reputation: 224942
Division by 0 is undefined behavior.
From section 5.6 of the C++ standard (C++11):
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 quotienta/b
is representable in the type of the result,(a/b)*b + a%b
is equal toa
.
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.
Upvotes: 8
Reputation: 11
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; 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.
Upvotes: 1
Reputation: 6946
This also depends on the floating point environment.
cppreference has details: http://en.cppreference.com/w/cpp/numeric/fenv (no examples though).
This should be available in most desktop/server C++11 and C99 environments. 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.
Upvotes: 0
Reputation: 63154
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 raiseddividing 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.
Upvotes: 33
Reputation: 180990
In [expr]/4 we have
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. [ Note: most existing implementations of C++ ignore integer overflows. 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.
Upvotes: 7