Handling overflow when casting doubles to integers in C

When casting floats to integers, overflow causes undefined behavior. From the C99 spec, section 6.3.1.4 Real floating and integer:

When a finite value of real floating type is converted to an integer type other than _Bool, the fractional part is discarded (i.e., the value is truncated toward zero). If the value of the integral part cannot be represented by the integer type, the behavior is undefined.

You have to check the range manually, but don’t use code like:

// DON'T use code like this!
if (my_double > INT_MAX || my_double < INT_MIN)
    printf("Overflow!");

INT_MAX is an integer constant that may not have an exact floating-point representation. When comparing to a float, it may be rounded to the nearest higher or nearest lower representable floating point value (this is implementation-defined). With 64-bit integers, for example, INT_MAX is 2^63 - 1 which will typically be rounded to 2^63, so the check essentially becomes my_double > INT_MAX + 1. This won’t detect an overflow if my_double equals 2^63.

For example with gcc 4.9.1 on Linux, the following program

#include <math.h>
#include <stdint.h>
#include <stdio.h>

int main() {
    double  d = pow(2, 63);
    int64_t i = INT64_MAX;
    printf("%f > %lld is %s\n", d, i, d > i ? "true" : "false");
    return 0;
}

prints

9223372036854775808.000000 > 9223372036854775807 is false

It’s hard to get this right if you don’t know the limits and internal representation of the integer and double types beforehand. But if you convert from double to int64_t, for example, you can use floating point constants that are exact doubles (assuming two’s complement and IEEE doubles):

if (!(my_double >= -9223372036854775808.0   // -2^63
   && my_double <   9223372036854775808.0)  // 2^63
) {
    // Handle overflow.
}

The construct !(A && B)also handles NaNs correctly. A portable, safe, but slighty inaccurate version for ints is:

if (!(my_double > INT_MIN && my_double < INT_MAX)) {
    // Handle overflow.
}

This errs on the side of caution and will falsely reject values that equal INT_MIN or INT_MAX. But for most applications, this should be fine.

Leave a Comment