# Can a conversion from double to int be written in portable C

• A+
Category：Languages

I need to write function like `double_to_int(double val, int *err)` which would covert double val to integer when it's possible; otherwise report an error (NAN/INFs/OUT_OF_RANGE).

so pseudo code implementation would look like:

``if isnan(val):     err = ERR_NAN     return 0 if val < MAX_INT:     err = ERR_MINUS_INF     return MIN_INT if ... return (int)val ``

There are at least two similar questions on SO: in this answer it's solved in enough clean way, though it's C++ solution - in C we do not have portable digits for signed int. In this answer, it's explained why we cannot just check `(val > INT_MAX || val < INT_MIN)`.

So the only possible clean way i see is to use floating point environment, but it's stated as implementation-defined feature.

So my question: is there any way to implement `double_to_int` function in cross-platform way (basing only on C standard, even not considering target platforms to support IEEE-754).?

Since conversion of `double` to `int` truncates toward zero, all the `double` values that properly convert to `int` are in the open interval (−`INT_MAX`−1, `INT_MAX`+1), and every value not inside this interval overflows when converted to `int` or is a NaN. We will find the `double` value `UpperBound` that is the greatest representable value less than `INT_MAX`+1 and the value `LowerBound` that is the least representable value greater than −`INT_MAX`−1. Then set of `double` values in the open interval (−`INT_MAX`−1, `INT_MAX`+1) equals set of `double` values in the closed interval [`LowerBound`, `UpperBound`], and we can test whether a value `x` is in the set by evaluating `LowerBound <= x && x <= UpperBound`.

The following determines `UpperBound`:

``static double UpperBound;  double b1 = INT_MAX, b0 = nexttoward(b1, 0); if (INT_MAX - (int) ceil(b0) < (int) (b1-b0))     UpperBound = b0; else if (INT_MAX - (int) ceil(b0) == (int) (b1-b0))     UpperBound = nexttoward(ceil(nexttoward(INT_MAX, HUGE_VALL)), 0); else     UpperBound = b1; ``

Reasoning:

• `INT_MAX` converts to some `double` `b1` that might be less than, equal to, or greater than `INT_MAX`. In any case, it must (assuming a sane implementation) be one of the values representable in `double` that equals or surrounds the mathematical value of `INT_MAX`. (In other words, conversion is not going to skip over a nearby value to give a worse result.) Whichever of those cases is the result, `b0` is the next lower representable value, so it must be less than `INT_MAX`. (If it were greater than `INT_MAX`, then `b1` is not one of the two representable values that surround `INT_MAX`.)
• `b1` is necessarily an integer; if its significand had bits of position value less than 1, then the significand has room to represent `INT_MAX` exactly, so the result of converting `INT_MAX` to `double` should have been exact.
• If `b0` is not an integer, `ceil(b0)`, `INT_MAX`, and `b1` are all equal. In this case, the middle case of the if-else-if-else is taken. That is discussed below. Otherwise, `b0` is an integer, and we continue with the following reasoning.
• `b1-b0` is exact, by Sterbenz’ Lemma. (More than that, they differ by 1 ULP, so that ULP is the result.)
• Since `b0` is an integer less than `INT_MAX`, it converts to `int` exactly.
• Since `b1-b0` is an integer less than `INT_MAX`, it converts to `int` exactly.
• If `INT_MAX - (int) b0` is less than `b1-b0`, then `b1` is greater than `INT_MAX`, and so `b0` is the largest `double` less than or equal to `INT_MAX`. (Note that, since `b0` is an integer, `INT_MAX - (int) b0` equals `INT_MAX - (int) ceil(b0)`.)
• If `INT_MAX - (int) b0` is greater than than `b1-b0`, then `b1` is less than or equal to `INT_MAX`, and it is the largest `double` less than or equal to `INT_MAX`.

Finally, we consider the case where `INT_MAX - (int) ceil(b0)` equals `b1-b0`. In this case, `b1` must equal `INT_MAX`, but there may be additional significand bits below the position value 1. For example, `INT_MAX`+1 may be a representable value. The reasoning here is:

• `b1` equals `INT_MAX`. We know this is true either because `b0` is an integer (it must be `INT_MAX`−1) and so the test in the `if` proves that `INT_MAX``b0` equals `b1``b0` or because `b0` is not an integer, in which case `ceil(b0)` must be `INT_MAX`, which means `INT_MAX` is representable, so converting it to `double` yields `INT_MAX`, and that is how we initialized `b1`.
• From `INT_MAX`, we move to the next lower representable value (which might be either a fraction or some integer amount greater than `INT_MAX`). Then we apply `ceil`. If we were at an integer, this has no effect. If we were not at an integer, the result is `INT_MAX`+1. Then we move to the next lower representable value. If we were at a fraction before, we are now at the greatest representable value less than `INT_MAX`+1. If we were at an integer before, we are now at `INT_MAX`, which is also, in this case, the greatest representable value less than `INT_MAX`+1.

`LowerBound` can be found from `INT_MIN` similarly.

The above does require that `INT_MAX` and `INT_MIN` be within the range of `double`. Thus, this could fail in implementation with a large `int` type and a very constrained non-IEEE-754 `double` type with no infinities. Of course, in such a system, all conversions from `double` to `int` are in range.