Division by Zero: Undefined Behavior or Implementation Defined in C And/Or C++

Division by zero: Undefined Behavior or Implementation Defined in C and/or C++?

I don't see any contradiction. Division by zero is undefined, period. There is no mention of "... unless INFINITY is defined" anywhere in the quoted text.

Note that nowhere in mathematics it is defined that 1 / 0 = infinity. One might interpret it that way, but it is a personal, "shortcut" style interpretation, rather than a sound fact.

Division by zero and undefined behaviour in C

The paper is wrong, and as for the clang example, this is a compiler bug (a rather common occurrence with clang...). I wish I could give you better reasons, but you already provided all the correct reasoning in the question.

Actually, for the clang issue, as far as I can tell, no bug has been demonstrated yet. Since bar does return in the example on the blog you linked to, the compiler is free to reorder the division across the call. This is trivial to do if bar is defined in the same translation unit, but it's also possible with LTO. To actually test for this bug, you need a function bar that never returns.

The behaviour of floating point division by zero

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.

Integer division by zero but system shows execution with exit code 0

This is undefined behavior. The compiler is free to do whatever it feels is right in this situation, for example terminating with exit code 0.

Why doesn't 'd /= d' throw a division by zero exception when d == 0?

C++ does not have a "Division by Zero" Exception to catch. The behavior you're observing is the result of Compiler optimizations:

  1. The compiler assumes Undefined Behavior doesn't happen
  2. Division by Zero in C++ is undefined behavior
  3. Therefore, code which can cause a Division by Zero is presumed to not do so.

    • And, code which must cause a Division by Zero is presumed to never happen
  4. Therefore, the compiler deduces that because Undefined Behavior doesn't happen, then the conditions for Undefined Behavior in this code (d == 0) must not happen
  5. Therefore, d / d must always equal 1.

However...

We can force the compiler to trigger a "real" division by zero with a minor tweak to your code.

volatile int d = 0;
d /= d; //What happens?

So now the question remains: now that we've basically forced the compiler to allow this to happen, what happens? It's undefined behavior—but we've now prevented the compiler from optimizing around this undefined behavior.

Mostly, it depends on the target environment. This will not trigger a software exception, but it can (depending on the target CPU) trigger a Hardware Exception (an Integer-Divide-by-Zero), which cannot be caught in the traditional manner a software exception can be caught. This is definitely the case for an x86 CPU, and most other (but not all!) architectures.

There are, however, methods of dealing with the hardware exception (if it occurs) instead of just letting the program crash: look at this post for some methods that might be applicable: Catching exception: divide by zero. Note they vary from compiler to compiler.

Handling division by zero with csignal results in unexpected behaviour

It's defined as:

The SIGFPE signal reports a fatal arithmetic error. Although the name is derived from “floating-point exception”, this signal actually covers all arithmetic errors, including division by zero and overflow. If a program stores integer data in a location which is then used in a floating-point operation, this often causes an “invalid operation” exception, because the processor cannot recognize the data as a floating-point number.

There's no reason for it to be labelled specifically FPE but these sorts of labels can evolve in unpredictable ways. I wouldn't read too much into it.

These signals are part of the POSIX standard and may not be fully supported or implemented in Windows. The Windows implementation of these support facilities is lacking in a number of areas, like how fork() is unsupported.

A few things about division by zero in C

Floating point division by zero behaves differently than integer division by zero.

The IEEE floating point standard differentiates between +inf and -inf, while integers cannot store infinity. Integer division by zero results in undefined behaviour. Floating point division by zero is defined by the floating point standard and results in +inf or -inf.

Edit:

As pointed out by Luchian, C++ implementations are not required to follow the IEEE Floating point standard. If the implementation you use doesn't follow the IEEE Floating point standard the result of floating point division by zero is undefined.

Is unevaluated division by 0 undefined behavior?

No.


Quotes from N4140:

§5.16 [expr.cond]/1

Conditional expressions group right-to-left. The first expression is
contextually converted to bool. It is evaluated and if it is true, the
result of the conditional expression is the value of the second
expression, otherwise that of the third expression. Only one of the
second and third expressions is evaluated.

Further:

§5 [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.

This clearly does not happen here. The same paragraph mentions division by zero explicitly in a note, and, although it is non-normative, it's making it even more clear that its pertinent to this situation:

[ 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 ]


There's also circumstantial evidence reinforcing the above point: the conditional operator is used to conditionally make behavior undefined.

§8.5 [dcl.init]/12.3

int f(bool b) {
unsigned char c;
unsigned char d = c; // OK, d has an indeterminate value
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if b is true
}

In the above example, using d to initialize int (or anything other than unsigned char) is undefined. Yet it is clearly stated that the UB occurs only if the UB branch is evaluated.


Going out of language-lawyer perspective: if this could be UB, then any division could be treated as UB, since the divisor could potentially be 0. This is not the spirit of the rule.



Related Topics



Leave a reply



Submit