Reputation: 14006
I have been given a legacy code, where someone(s) have carelessly assigned double
values to int
variables like:
int a = 10;
double b = 20;
a = b;
Now to get rid of the
warning C4244: '=': conversion from 'double' to 'int', possible loss of data
warnings, I tried editing the code upstream and get rid of the unnecessary double
variables, but it turns out to be too messy!
I could also use casting:
a = (int) b;
but actually there is no guarantee that b
will be within the limits of an integer. I thought of making a helper function:
int safeD2I(double inputVar){
if ((inputVar < INT_MAX) && (INT_MIN < inputVar)){
return (int) inputVar;
} else {
exit(-1);
}
}
but I'm not sure if this is the best implementation. I was wondering if there is a more canonical way to handle this issue?
what I want:
b
variable being outside of the integer bounds, the program stops immediatelyThanks for your support in advance.
Upvotes: 2
Views: 2399
Reputation: 153457
what is the safe way to convert double to int?
if ((inputVar < INT_MAX) && (INT_MIN < inputVar)){
fails edge cases.
Wrong edges as it is more like, but not exactly like (inputVar < INT_MAX + 1) && (INT_MIN - 1 < inputVar)
Be wary of code like some_FP < SOME_INT_MAX
as SOME_INT_MAX
may not convert to the FP value needed due to the integer type may have more precision that the FP one. This is not usually a problem with int, double
.
Test if the double
is within the range of (INT_MIN-1 .... INT_MAX+1)
1. Note () and not [].
If not, error out or handle in some defined way of your choosing.
Assuming typical 2's complement, but not assuming the precision of double
exceeds int
(more useful to migrate code to float, long long
that way), some sample code:
// FP version of INT_MAX + 1.0
// Avoid direct (INT_MAX + 1.0) as that can have precision woes
#define DBL_INTMAX_P1 ((INT_MAX/2 + 1)*2.0)
int X_int_from_double(double x) {
// Coded to insure NAN fails the if()
if (!(x - INT_MIN > -1.0 && x < DBL_INTMAX_P1)) {
errno = ERANGE;
fprintf(stderr, "Error in %s, %.*e too large\n", __func__, DBL_DECIMAL_DIG - 1, x);
exit(EXIT_FAILURE);
// or additional code to handle conversion in some specified manner
// Example: assuming "wrap"
if (!isfinite(x)) {
if (!isnan(x)) return 0;
if (x > 0) return INT_MAX;
else return INT_MIN;
}
modf(x, &x); // drop fraction
x = fmod(x, DBL_INTMAX_P1*2);
if (x >= DBL_INTMAX_P1) x -= DBL_INTMAX_P1*2;
else if (x < -DBL_INTMAX_P1) x += DBL_INTMAX_P1*2;
}
return (int) x;
}
To record the line where this failed, consider a macro to pass the line number.
int X_int_from_double(double x, unsigned);
#define DOUBLE_TO_INT(x) X_int_from_double((x), __LINE__)
1 Example -2,147,483,648.9999... to 2,147,483,647.9999...
Upvotes: 2
Reputation: 215261
First, there's nothing inherently wrong or unsafe with the code as-is. Conversion by assignment is a completely legitimate part of the C language. If this is legacy code where you don't know if it's being done safely or not, you need to do the analysis to determine that.
If you find that you do need to catch possible out-of-bounds values (which produce undefined behavior (!!) when converted to int
), your code is both wrong and fragile. Comparisons like:
double x;
...
if (x < INT_MAX) ...
coerce INT_MAX
to type double
for the comparison. In practice in a world where double
is IEEE double and int
is 32-bit, this happens to be safe, but it would be unsafe for example if you changed double
to float
, since a 32-bit INT_MAX
is not representable in single-precision float
. The value will be rounded, and then the comparison takes place after the rounding.
Now, it turns out you also have an off-by-one error (<= INT_MAX
, not < INT_MAX
, is what's in-bounds) as well as incorrect logic (||
instead of &&
) so the rounding would actually fix part of that. But it's not right to depend on it. Instead you need to manufacture a power of two you can compare against, so it's safe to convert to floating point. For example:
if (x < 2.0*(INT_MAX/2+1) && x >= INT_MIN)
if (-x > (-INT_MAX-1) && x >= INT_MIN)
if (-x > INT_MIN && x >= INT_MIN)
These all assume INT_MIN
is actually a power of two (full range twos complement) which is a reasonable real-world assumption and required by C2x+. If you want more generality it's more work.
Finally, I first wrote this as a comment on your question, but the more I think about it, it really belongs in an answer: Based on what your C proficiency seems to be, I would be very wary of making any unnecessary or "cleanup" changes to this code. You are likely to break far more than you fix. Make changes only where you've done analysis to determine that there's an active bug, and don't refactor things or change types to fix. Make simple direct inline fixes.
Upvotes: 5