- A+

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.