Home > front end >  Can a floating-point conversion cause undefined behavior?
Can a floating-point conversion cause undefined behavior?

Time:11-16

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.

  • Related