constexpr
initializers (C2x CD UK comment)All subclause and paragraph references here are to the C2x CD, SC22
N5777. Throughout, it is supposed that Annex F is in effect for
binary floating-point arithmetic, so float
and double
have the IEC 60559 binary32 and binary64
formats.
6.7.1 paragraph 5 says, regarding initializers
for constexpr
objects, says:
The value of any constant expressions or of any character in a string literal of the initializer shall be exactly representable in the corresponding target type; no change of value shall be applied.
with a footnote saying
In the context of arithmetic conversions, 6.3.1 describes the details of changes of value that occur if values of arithmetic expressions are stored in the objects that for example have a different signedness, excess precision or quantum exponent. Whenever such a change of value is necessary, the constraint is violated.
6.3.1 does not define the concepts of “exactly
representable” or “change of value”. To some
extent, it uses those concepts (without a precise definition).
However, various ambiguities arise in the interpretation of those
concepts for constexpr
initializers of objects of (real
or complex) floating type, that do not arise in 6.3.1, at least when
6.3.1 is taken together with the more detailed semantics in
Annex F. Subclause 6.3.1 (plus Annex F) only has to define
what the result of a conversion is; it doesn’t have to define
whether values in one type are considered the same as values in
another type.
In particular, the concept of “represented exactly” as used in 6.3.1.5 is considered as one of a group of cases with “in the range of values that can be represented but cannot be represented exactly” and “outside the range of values that can be represented”. This does not cover the cases of NaNs (not ordered, so not part of any range), or of additional information beyond that determined by the ordering (sign of zero, quantum exponent). Furthermore, the rules for conversions between real and complex types in 6.3.1.7 never need to say whether a value is considered exactly representable in another type; they simply say what the result of a conversion is.
Thus, consider the following specific questions and examples. In each case, WG14 needs to decide what the desired semantics are, and ensure that normative text (not just footnotes and examples) is sufficient to make those semantics clear. This should probably be done based on recommendations from the C floating-point group, and much of the normative text might reasonably go in Annex F (maybe F.8.5) and Annex G; I have previously suggested that defining a term “constexpr-representable” might be helpful. For some of the questions, the answers might be different in different sub-cases; the examples try to illustrate the main possible sub-cases.
Question 1: Suppose a constexpr
object of complex type is initialized with an expression of real type
(including the case of integer type), and that value is exactly
representable (whatever that means) in the corresponding real type.
Does this count as exactly representable in the complex type, or is
(x, 0) considered as a different value from the real
number x (one has an imaginary part (of 0), the other
doesn’t), so resulting in a constraint violation?
constexpr _Complex double x1 = 1.0;
Question 2: Suppose a constexpr
object of real floating type is initialized with an expression of
complex type, and the real part of the expression is exactly
representable (whatever that means) in the type of the object being
initialized, and the imaginary part is the same (positive or unsigned)
zero that results from a conversion from real to complex type. Does
this count as exactly representable in the real type, or is
(x, 0) considered as a different value from the real
number x, so resulting in a constraint violation?
constexpr double x2 = (_Complex double) 1.0;
Question 3: If the example in Question 2 is valid, what about if the zero is a different one from that resulting from conversion from real to complex (for example, if it is a negative zero)? Is this valid or a constraint violation?
#include <complex.h> constexpr double x3 = CMPLX (1.0, -0.0);
Question 4: Is a quiet NaN of one standard floating type considered exactly representable in another standard floating type? Or is this only guaranteed in some cases, depending on whether the payload can be presented in both types? Does it depend in some way on how the implementation defines conversions between NaNs in different types?
#include <float.h> constexpr double x4a = NAN; constexpr long double x4b = NAN; constexpr float x4c = (double) NAN; constexpr float x4d = (long double) NAN;
Question 5: Is a signaling NaN of one standard floating type considered exactly representable in another standard floating type? There is a strong argument that it should not be valid as an initializer if the two types are of different formats, because the implicit conversion would produce a quiet NaN. If the two types are of the same format, does validity depend on whether the conversion is a convertFormat or copy operation?
#include <float.h> constexpr double x5a = FLT_SNAN; // Does validity of these examples depend on whether double and long // double have the same format, and, if they do, whether the // conversion is a convertFormat or copy operation? constexpr long double x5b = DBL_SNAN; constexpr double x5c = LDBL_SNAN;
Question 6: Suppose a constexpr
object of decimal floating type is initialized with an expression of
standard or binary floating type, and that the value represented by
that expression at IEEE Level 2 (“Floating-point
data”) can also be represented in the decimal floating type. Is
this valid, or is it a constraint violation because of differences at
IEEE Level 3 (which represents quantum exponents, which
don’t exist in binary types)? Does it make any difference if
the value is an infinity or (quiet) NaN (no quantum exponents)? And
what about integer values?
#include <float.h> constexpr _Decimal32 x6a = 1.0; constexpr _Decimal32 x6b = INFINITY; constexpr _Decimal128 x6c = NAN; constexpr _Decimal32 x6d = 1;
Question 7: Suppose a constexpr
object of standard or binary floating type is initialized with an
expression of decimal floating type, and that the value represented by
that expression at IEEE Level 2 (“Floating-point
data”) can also be represented in the standard or binary
floating type. Is this valid, or is it a constraint violation because
of differences at IEEE Level 3 (which represents quantum
exponents, which don’t exist in binary types)? Does it make any
difference if the value is an infinity or (quiet) NaN (no quantum
exponents)? For finite values, does it make any difference whether
the quantum exponent is the one that would result from a conversion in
the reverse direction?
#include <float.h> constexpr float x7a = 1.DF; constexpr float x7b = 1.00DF; constexpr float x7c = DEC_INFINITY; constexpr double x7d = DEC_NAN;
Question 8: Suppose both
the constexpr
object and the expression initializing it
have decimal floating type, and the real number represented by the
expression is representable in the type of the object, but the quantum
exponent is not representable. Is this valid? The example in 6.7.1
paragraph 14 (Note 2) is commented to say not, but it is not
clear this follows from any normative text.
#include <float.h> constexpr _Decimal32 x8 = DEC64_TRUE_MIN * 0;