A floating-point conversion, as the standard defines it, is a conversion between two floating-point types that isn't a promotion.
The simplest example is double
to float
:
double d = 0.1;
float f = d;
The standard says [conv.double]:
A prvalue of floating-point type can be converted to a prvalue of another floating-point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.
The conversions allowed as floating-point promotions are excluded from the set of floating-point conversions.
In my example above, the source value cannot be exactly represented in the destination type. The value of d
is 0.10000000000000001
, whereas the value of f
is (likely) 0.10000000149011612
, and indeed if you cast f
back to a double
, is doesn't equal d
. However, this source value is between two adjacent destination values: f
and the previous representable float
value, 0.099999994039535522
. So the value of f
can be either of these values, but because 0.10000000149011612
is closer to 0.10000000000000001
than 0.099999994039535522
is, that's likely the value chosen by the implementation.
My question is about the last case:
Otherwise, the behavior is undefined.
Are there any values for which a conversion is undefined behavior? Since floating-point types have representations for infinity and -infinity, I would assume there cannot be any source value that isn't exactly represented or between two adjacent destination values: any double
value is either an exact float
value (including NaN
) or between -infinity and infinity, in which case it is between two adjacent float
values.
So what is the point of this "otherwise" case? Is it here to cover exotic types that are considered floating-point but aren't float
, double
, or long double
? Can a conversion between float
, double
, and long double
cause undefined behavior?
CodePudding user response:
It turns out some floating-point implementations cannot represent infinities. MBF, as Eljay pointed out, is one of them. It's also implied by the existence of HUGE_VAL
, which is the same as INFINITY
if possible.
However, this is extremely unlikely, and can be tested with std::numeric_limits<T>::has_infinity
. Presumably, if this value is true, then there cannot be any undefined behavior with floating-point conversions.
CodePudding user response:
Can a floating-point conversion cause undefined behavior?
Yes.
Consider 2 float
values: FLT_MAX
and nextafterf(FLT_MAX, 0)
. Their difference is delta
. All saved in a double
.
random(a, b)
forms a double
random value (a, b].
double max = FLT_MAX;
double max_before = nextafterf(FLT_MAX, 0);
double delta = max - max_before;
// Conversion to `float` is well defined
double in_between = max_before random(0.0, delta);
float in_betweenf = in_between; // in_between is inclusively between 2 float.
// Conversion to `float` can fail as the `double` value can
// exceed FLT_MAX, even is the sum is the smallest `double` more than `FLT_MAX`.
double in_between = max random(0.0, delta);
float in_betweenf = in_between;
This is primarily the case when float
does not support infinity.
If the value being converted is outside the range of values that can be represented, the behavior is undefined. C17dr § 6.3.1.5 1.
Ideally it would be nice if in_between = max_before delta random(0.0, 0.5*delta);
was well defined, but it is not when float
lacks an infinity.