Question

Why is floating-point zero-divide forbidden in a constexpr?

Using the g++ compiler, both of these compile fine:

constexpr double d1 = 2.0 / 1.0;
const double d2 = 2.0 / 0.0;

But this does not:

constexpr double d2 = 2.0 / 0.0;

The compile-time error is:

error: ‘(2.0e+0 / 0.0)’ is not a constant expression

Note that cppreference gives this exact example, confirming that it's not allowed.

I don't understand why it's not allowed. Keeping in mind that std::numeric_limits::is_iec559 is constexpr, the compiler clearly knows at compile time, within a constexpr, whether divide-by-zero is allowed. So, in the case that it is allowed... why isn't it allowed within the constexpr?

EDIT: It was suggested that this question is siimilar to The behaviour of floating point division by zero . My question here is intended to be more focused (why do the first two expressions appear to be allowed, but the third disallowed)? The answers to the other question don't seem to shed light on that: some of the answers seem to imply all three expressions are UB (and therefore are disallowed? I don't think I believe that; but in any case there's something specific about constexpr that seems to make a difference, thus this question which is focused on the constexpr).

This question is also similar to Compile time floating point division by zero in C++ whose accepted answer (essentially "it's UB so anything goes") doesn't help at all.

 6  246  6
1 Jan 1970

Solution

 5

The ISO C++ standard explicitly specifies that division by zero has undefined behavior, for both integral and floating point types.

See [expr.mul]/4.

If evaluation of an expression would have undefined behavior according to the core language specification, then an expression whose evaluation would involve that evaluation, is specified to not be a (core) constant expression.

Therefore any division by zero must be impossible at compile-time.

Although division by zero is undefined per C++ standard, typically implementations of floating point types adhere to IEEE 754, which defines the result of dividing by zero. That's of course fine. If a program has undefined behavior according to the C++ standard, then the compiler is free to give it any semantic. In particular it can promise to adhere to stricter rules, e.g. another specification.

However, inside constant expressions the language is intentionally restricted to what is explicitly defined by the standard. Compilers can't extent the behavior of constexpr as they can at runtime. If a constexpr variable's initialization is not a constant expression, then the program is ill-formed.

Although, the standard never requires a compiler to fail compilation. If the program is ill-formed, then the only requirement is that the compiler shall emit one diagnostic. This could just as well be just a warning and then the compiler could simply perform the division at compile-time in the same way that it would at runtime.

In certain contexts, rather than just well-formedness, the semantics of the program can be affected by whether or not an expression is a constant expression. In that case, the compiler is required to give the program the correct semantics. A warning and different behavior wouldn't be possible in that case.

2024-07-01
user17732522