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

前端 未结 7 760
借酒劲吻你
借酒劲吻你 2020-12-10 01:42

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/I

7条回答
  •  孤城傲影
    2020-12-10 02:26

    Yes. (nan/inf handling omitted for brevity)

    int convert(double x) {
       if (x == INT_MAX) {
         return INT_MAX;
       } else if (x > INT_MAX) {
         err = ERR_OUT_OF_RANGE; 
         return INT_MAX;
       } else if (x == INT_MIN) {
         return INT_MIN;
       } else if (x < INT_MIN)
         err = ERR_OUT_OF_RANGE;
         return INT_MIN;
       } else {
         return x;
       }
    }
    

    Explanation.

    The edge cases, as explained in one of the linked answers, are when INT_MAX is not representable as double exactly, and is rounded up when converted to double, and a symmetric case one with INT_MIN. That's the case when if (x > INT_MAX) fails. That is, the comparison returns false, but we still cannot convert x to int directly.

    What the linked answer fails to recognise is that there's only one double number that fails the test, namely (double)INT_MAX, and we can easily catch this case by checking for x == INT_MAX explicitly.

    Edit As noted in the comments, this may fail if INT_MAX or INT_MIN is outside of the range of double. While extremely unlikely, this is not precluded by the standard. In such an implementation, the conversion is just (int)x. It should be easier to detect such an implementation at configuration time than at run time. If the latter is absolutely needed, one can perform this operation once:

    static int need_simple_conversion = 0;
    char* str = malloc(sizeof(int)*CHAR_BIT+1);
    sprintf (str, "%d", INT_MAX);
    errno = 0;
    if (strtod(s, NULL) == HUGE_VAL && errno == ERANGE) {
       // INT_MAX overflows double => double can never overflow int
       need_simple_conversion = 1;
    }
    

    Then

    if (need_simple_conversion)
        return x;
    else { // as above
    

    For the paranoid among us, do this with INT_MIN too and perform the check separately for positive and negative doubles.

提交回复
热议问题