I am looking to check if a double
value can be represented as an int
(or the same for any pair of floating point an integer types). This is a simple way to do it:
double x = ...;
int i = x; // potentially undefined behaviour
if ((double) i != x) {
// not representable
}
However, it invokes undefined behaviour on the marked line, and triggers UBSan (which some will complain about).
Questions:
Clarifications, as requested:
The situation I am facing right now involves conversion from double
to various integer types (int
, long
, long long
) in C. However, I have encountered similar situations before, thus I am interested in answers both for float -> integer and integer -> float conversions.
Examples of how the conversion may fail:
3.5
.1.23e100
.double
have 52 binary digits compared to 63 digits in a 64-bit integer type. For example, on a typical 64-bit system, (long) (double) ((1L << 53) + 1L)
.1L << 53
(as opposed to (1L << 53) + 1
) is technically exactly representable as a double
, and that the code I proposed would accept this conversion, even though it probably shouldn't be allowed.Create range limits exactly as FP types
The "trick" is to form the limits without loosing precision.
Let us consider float
to int
.
Conversion of float
to int
is valid (for example with 32-bit 2's complement int
) for -2,147,483,648.9999... to 2,147,483,647.9999... or nearly INT_MIN
-1 to INT_MAX
+ 1.
We can take advantage that integer_MAX
is always a power-of-2 - 1 and integer_MIN
is -(power-of-2) (for common 2's complement).
Avoid the limit of FP_INT_MIN_minus_1
as it may/may not be exactly encodable as a FP.
// Form FP limits of "INT_MAX plus 1" and "INT_MIN"
#define FLOAT_INT_MAX_P1 ((INT_MAX/2 + 1)*2.0f)
#define FLOAT_INT_MIN ((float) INT_MIN)
if (f < FLOAT_INT_MAX_P1 && f - FLOAT_INT_MIN > -1.0f) {
// Within range.
Use modff() to detect a fraction if desired.
}
More pedantic code would use !isnan(f)
and consider non-2's complement encoding.