The underlying problem is to find min_double_to_int and max_double_to_int, the smallest and largest double, respectively, that can be converted to an int.
The portable conversion function itself can be written in C11 as
int double_to_int(const double value, int *err) { if (!isfinite(value)) { if (isnan(value)) { if (err) *err = ERR_NAN; return 0; } else if (signbit(value)) { if (err) *err = ERR_NEG_INF; return INT_MIN; } else { if (err) *err = ERR_POS_INF; return INT_MAX; } } if (value < min_double_to_int) { if (err) *err = ERR_TOOSMALL; return INT_MIN; } else if (value > max_double_to_int) { if (err) *err = ERR_TOOLARGE; return INT_MAX; } if (err) *err = 0; return (int)value; }
Before the above function is first used, we need to assign min_double_to_int and max_double_to_int.
EDITED on 2018-07-03: Rewritten approach.
We can use a simple function to find the smallest power of ten that is at least as large as INT_MAX/INT_MIN in magnitude. If those are smaller than DBL_MAX_10_EXP, the range of double is greater than the range of int, and we can cast INT_MAX and INT_MIN to double.
Otherwise, we construct a string containing the decimal representation of INT_MAX/INT_MIN, and use strtod() to convert them to double. If this operation overflows, it means the range of double is smaller than the range of int, and we can use DBL_MAX/-DBL_MAX as max_double_to_int and min_double_to_int, respectively.
When we have INT_MAX as a double, we can use a loop to increment that value using nextafter(value, HUGE_VAL). The largest value that is finite, and rounded down using floor() still yields the same double value, is max_double_to_int.
Similarly, when we have INT_MIN as a double, we can use a loop to decrement that value using nextafter(value, -HUGE_VAL). The largest value in magnitude that is still finite, and rounds up (ceil()) to the same double, is min_double_to_int.
Here is an example program to illustrate this:
#include <stdlib.h> #include <limits.h> #include <string.h> #include <float.h> #include <stdio.h> #include <errno.h> #include <math.h> static double max_double_to_int = -1.0; static double min_double_to_int = +1.0; #define ERR_OK 0 #define ERR_NEG_INF -1 #define ERR_POS_INF -2 #define ERR_NAN -3 #define ERR_NEG_OVER 1 #define ERR_POS_OVER 2 int double_to_int(const double value, int *err) { if (!isfinite(value)) { if (isnan(value)) { if (err) *err = ERR_NAN; return 0; } else if (signbit(value)) { if (err) *err = ERR_NEG_INF; return INT_MIN; } else { if (err) *err = ERR_POS_INF; return INT_MAX; } } if (value < min_double_to_int) { if (err) *err = ERR_NEG_OVER; return INT_MIN; } else if (value > max_double_to_int) { if (err) *err = ERR_POS_OVER; return INT_MAX; } if (err) *err = ERR_OK; return (int)value; } static inline double find_double_max(const double target) { double next = target; double curr; do { curr = next; next = nextafter(next, HUGE_VAL); } while (isfinite(next) && floor(next) == target); return curr; } static inline double find_double_min(const double target) { double next = target; double curr; do { curr = next; next = nextafter(next, -HUGE_VAL); } while (isfinite(next) && ceil(next) == target); return curr; } static inline int ceil_log10_abs(int value) { int result = 1; while (value < -9 || value > 9) { result++; value /= 10; } return result; } static char *int_string(const int value) { char *buf; size_t max = ceil_log10_abs(value) + 4; int len; while (1) { buf = malloc(max); if (!buf) return NULL; len = snprintf(buf, max, "%d", value); if (len < 1) { free(buf); return NULL; } if ((size_t)len < max) return buf; free(buf); max = (size_t)len + 2; } } static int int_to_double(double *to, const int ivalue) { char *ival, *iend; double dval; ival = int_string(ivalue); if (!ival) return -1; iend = ival; errno = 0; dval = strtod(ival, &iend); if (errno == ERANGE) { if (*iend != '\0' || dval != 0.0) { /* Overflow */ free(ival); return +1; } } else if (errno != 0) { /* Unknown error, not overflow */ free(ival); return -1; } else if (*iend != '\0') { /* Overflow */ free(ival); return +1; } free(ival); /* Paranoid overflow check. */ if (!isfinite(dval)) return +1; if (to) *to = dval; return 0; } int init_double_to_int(void) { double target; if (DBL_MAX_10_EXP > ceil_log10_abs(INT_MAX)) target = INT_MAX; else { switch (int_to_double(&target, INT_MAX)) { case 0: break; case 1: target = DBL_MAX; break; default: return -1; } } max_double_to_int = find_double_max(target); if (DBL_MAX_10_EXP > ceil_log10_abs(INT_MIN)) target = INT_MIN; else { switch (int_to_double(&target, INT_MIN)) { case 0: break; case 1: target = -DBL_MAX; break; default: return -1; } } min_double_to_int = find_double_min(target); return 0; } int main(void) { int i, val, err; double temp; if (init_double_to_int()) { fprintf(stderr, "init_double_to_int() failed.\n"); return EXIT_FAILURE; } printf("(int)max_double_to_int = %d\n", (int)max_double_to_int); printf("(int)min_double_to_int = %d\n", (int)min_double_to_int); printf("max_double_to_int = %.16f = %a\n", max_double_to_int, max_double_to_int); printf("min_double_to_int = %.16f = %a\n", min_double_to_int, min_double_to_int); temp = nextafter(max_double_to_int, 0.0); for (i = -1; i <= 1; i++) { val = double_to_int(temp, &err); printf("(int)(max_double_to_int %+d ULP)", i); switch (err) { case ERR_OK: printf(" -> %d\n", val); break; case ERR_POS_OVER: printf(" -> overflow\n"); break; case ERR_POS_INF: printf(" -> infinity\n"); break; default: printf(" -> BUG\n"); } temp = nextafter(temp, HUGE_VAL); } temp = nextafter(min_double_to_int, 0.0); for (i = 1; i >= -1; i--) { val = double_to_int(temp, &err); printf("(int)(min_double_to_int %+d ULP)", i); switch (err) { case ERR_OK: printf(" -> %d\n", val); break; case ERR_NEG_OVER: printf(" -> overflow\n"); break; case ERR_NEG_INF: printf(" -> infinity\n"); break; default: printf(" -> BUG\n"); } temp = nextafter(temp, -HUGE_VAL); } return EXIT_SUCCESS; }
frexpis any help.