Fred Tydeman wrote:
>> How about places where 'float' promotes to 'double' and '_Float32' does not promote? Such as printf()?
Good point. Thanks. The proposed rules for floating-point promotion are the same in both languages, where float is the only thing that ever promotes to double. So the difference in usual arithmetic conversions means that:
printf("%g", (float)1.0 + (_Float32)2.0);
is undefined behavior in C (because the expression has type _Float32, which does not promote), but
printf("%g", (float)1.0 + (std::float32_t)2.0);
is well-formed in C++ (because the expression has type float, which does promote).
I see this as an argument for C to change the rules so that
float + _Float32 -> float
Not a compelling argument by itself, but a point in favor of the change.
I couldn't believe this was true, but after looking it up in N2596 6.5.2.2, I see:
If the expression that denotes the called function has a type that does not include a prototype,
the integer promotions are performed on each argument, and arguments that have type float
are promoted to double. These are called the default argument promotions. If the number of
arguments does not equal the number of parameters, the behavior is undefined. If the
function is defined with a type that includes a prototype, and either the prototype ends with
an ellipsis (, ...) or the types of the argument after promotion are not compatible with the
types of the parameters, the behavior is undefined.
And that's quite alarming; it means that one of these is UB and the other is not, even though float and _Float32 will have the same representation on most of today's compilers:
float fa;
printf("%g\n", fa);
_Float32 fb;
printf("%g\n", fb);
And it also is a pain for anyone wanting to print _Float16 values:
_Float16 f16;
printf("%g\n", f16); // UB
printf("%g\n", (double)f16); // OK
It seems very much against the spirit of the automatic-float-to-double conversion rule, for printf to work with float, but not _Float16 or _Float32 or even _Float64. Surely this was an oversight on C's part? It seems the wording should be changed:
< arguments that have type float are promoted to double
> floating-point arguments that have less or equal range and precision than double are promoted to double
(The "or equal" part is possibly unnecessary and reflects my imperfect understanding of the consequences of passing _Float64 to a routine that expects double, including whether those consequences differ from C to C++)