【问题标题】:Can a conversion from double to int be written in portable C可以用便携式C编写从double到int的转换吗
【发布时间】:2018-12-08 20:11:22
【问题描述】:

我需要编写像double_to_int(double val, int *err) 这样的函数 可能时将 double val 转换为整数;否则报错(NAN/INFs/OUT_OF_RANGE)。

所以伪代码实现看起来像:

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

关于 SO 至少有两个类似的问题: 在this 回答中,尽管它是 C++ 解决方案,但它已经以足够干净的方式解决了 - 在 C 中,我们没有用于有符号 int 的可移植数字。 在this 的回答中,解释了为什么我们不能只检查(val &gt; INT_MAX || val &lt; INT_MIN)

所以我看到的唯一可能的干净方式是使用浮点环境,但它被声明为实现定义的功能。

所以我的问题是:有什么方法可以跨平台实现double_to_int 功能(仅基于C标准,甚至不考虑 目标平台支持 IEEE-754)。?

【问题讨论】:

  • “请在标记为重复之前阅读。”应该去评论区
  • 不知道frexp有什么帮助。
  • 我真的觉得你的问题是 the answer 你在问题中链接的答案,因此让你的问题重复。
  • 你真的应该解释为什么 Stargateur 显示的答案没有回答你的问题。
  • 我觉得所有的“紧密重复”都会在他们的答案中的某个时刻故障转移到特定的实现。我坚信不可能做到这一点;我的回答只不过是邀请同行评审。

标签: c floating-point standards


【解决方案1】:

[已使用全新方法编辑此答案。]

这种方法使用 C 标准中浮点格式的定义——作为带符号的基数-b 数字乘以 b 的幂。知道有效数字中的位数(由DBL_MANT_DIG 提供)和指数限制(由DBL_MAX_EXP 提供)允许我们准备精确的double 值作为端点。

我相信它适用于所有符合标准的 C 实现,但须遵守初始评论中所述的适度附加要求。

/*  This code demonstrates safe conversion of double to int in which the
    input double is converted to int if and only if it is in the supported
    domain for such conversions (the open interval (INT_MIN-1, INT_MAX+1)).
    If the input is not in range, an error is indicated (by way of an
    auxiliary argument) and no conversion is performed, so all behavior is
    defined.

    There are a few requirements not fully covered by the C standard.  They
    should be uncontroversial and supported by all reasonable C implementations:

        Conversion of an int that is representable in double produces the
        exact value.

        The following operations are exact in floating-point:

            Dividing by the radix of the floating-point format, within its
            range.

            Multiplying by +1 or -1.

            Adding or subtracting two values whose sum or difference is
            representable.

        FLT_RADIX is representable in int.

        DBL_MIN_EXP is not greater than -DBL_MANT_DIG.  (The code can be
        modified to eliminate this requirement.)

    Deviations from the requested routine include:

        This code names the routine DoubleToInt instead of double_to_int.

        The only error indicated is ERANGE.  Code to distinguish the error more
        finely, such as providing separate values for NaNs, infinities, and
        out-of-range finite values, could easily be added.
*/


#include <float.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>


/*  These values will be initialized to the greatest double value not greater
    than INT_MAX+1 and the least double value not less than INT_MIN-1.
*/
static double UpperBound, LowerBound;


/*  Return the double of the same sign of x that has the greatest magnitude
    less than x+s, where s is -1 or +1 according to whether x is negative or
    positive.
*/
static double BiggestDouble(int x)
{
    /*  All references to "digits" in this routine refer to digits in base
        FLT_RADIX.  For example, in base 3, 77 would have four digits (2212).

        In this routine, "bigger" and "smaller" refer to magnitude.  (3 is
        greater than -4, but -4 is bigger than 3.)
    */

    //  Determine the sign.
    int s = 0 < x ? +1 : -1;

    //  Count how many digits x has.
    int digits = 0;
    for (int t = x; t; ++digits)
        t /= FLT_RADIX;

    /*  If the double type cannot represent finite numbers this big, return the
        biggest finite number it can hold, with the desired sign.
    */
    if (DBL_MAX_EXP < digits)
        return s*DBL_MAX;

    //  Determine whether x is exactly representable in double.
    if (DBL_MANT_DIG < digits)
    {
        /*  x is not representable, so we will return the next lower
            representable value by removing just as many low digits as
            necessary.  Note that x+s might be representable, but we want to
            return the biggest double less than it, which is also the biggest
            double less than x.
        */

        /*  Figure out how many digits we have to remove to leave at most
            DBL_MANT_DIG digits.
        */
        digits = digits - DBL_MANT_DIG;

        //  Calculate FLT_RADIX to the power of digits.
        int t = 1;
        while (digits--) t *= FLT_RADIX;

        return x / t * t;
    }
    else
    {
        /*  x is representable.  To return the biggest double smaller than
            x+s, we will fill the remaining digits with FLT_RADIX-1.
        */

        //  Figure out how many additional digits double can hold.
        digits = DBL_MANT_DIG - digits;

        /*  Put a 1 in the lowest available digit, then subtract from 1 to set
            each digit to FLT_RADIX-1.  (For example, 1 - .001 = .999.)
        */
        double t = 1;
        while (digits--) t /= FLT_RADIX;
        t = 1-t;

        //  Return the biggest double smaller than x+s.
        return x + s*t;
    }
}


/*  Set up supporting data for DoubleToInt.  This should be called once prior
    to any call to DoubleToInt.
*/
static void InitializeDoubleToInt(void)
{
    UpperBound = BiggestDouble(INT_MAX);
    LowerBound = BiggestDouble(INT_MIN);
}


/*  Perform the conversion.  If the conversion is possible, return the
    converted value and set *error to zero.  Otherwise, return zero and set
    *error to ERANGE.
*/
static int DoubleToInt(double x, int *error)
{
    if (LowerBound <= x && x <= UpperBound)
    {
        *error = 0;
        return x;
    }
    else
    {
        *error = ERANGE;
        return 0;
    }
}


#include <string.h>


static void Test(double x)
{
    int error, y;
    y = DoubleToInt(x, &error);
    printf("%.99g -> %d, %s.\n", x, y, error ? strerror(error) : "No error");
}


#include <math.h>


int main(void)
{
    InitializeDoubleToInt();
    printf("UpperBound = %.99g\n", UpperBound);
    printf("LowerBound = %.99g\n", LowerBound);

    Test(0);
    Test(0x1p31);
    Test(nexttoward(0x1p31, 0));
    Test(-0x1p31-1);
    Test(nexttoward(-0x1p31-1, 0));
}

【讨论】:

  • @NominalAnimal:一个有趣的想法,我会考虑转换为unsigned int是否会给我们一些余地。但是,由doubleunsigned int 的转换引起的转换不一定是模数。根据 C 2011 (N1570) 6.3.1.4 注释 61,“当实浮点类型的值转换为无符号类型时,不需要执行将整数类型的值转换为无符号类型时执行的求余运算。因此,可移植实浮点值的范围是 (−1, Utype_MAX+1)。”
  • limits.h 定义 INT_MAXINT_MIN
  • @BobJarvis:你的意思是什么?在int 中获取INT_MAXINT_MIN 没有任何问题。问题是我们不知道它们可以正确地转换为double,但我们需要找到小于INT_MAX+1 的最大double。所以我们需要想办法规避或纠正转换过程中可能出现的舍入误差。
  • @EricPostpischil:我重写了我的答案以在nextafter() 循环中使用floor(max_double_to_int) == (double)INT_MAXceil(min_double_to_int) == (double)INT_MIN,并通过strtod() 处理DBL_MAX &lt;= INT_MAX-DBL_MAX &gt;= INT_MIN 时的奇数情况。
  • 请求是针对“portable C”的,这就引发了“哪一个?”的问题。 AFAIK,nexttoward(..) 首次出现在 C99;它不在 ANSI C 中。nexttoward(..) 可以用 ANSI C 编写吗?
【解决方案2】:

可以用可移植的 C 编写从 double 到 int 的对话”的答案显然是“是的”。

例如,您可以将浮点值 sprintf 到一个字符串,进行基于字符串的检查(即通过基于字符串的比较您也 sprintf'd 的最大值和最小值)、验证、舍入等,然后 sscanf 已知- 最终值的有效字符串。

实际上,您将转向一种 (a) 可移植且 (b) 方便的中间表示。 C 字符串在可移植性方面很好,但不是那么方便。如果可以使用外部库,有几个比较方便,但要确认其可移植性。

例如(省略四舍五入):

#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <string.h>

int convert(double inVal) {
    // basic range check - does anybody have an integer format with more than 300 bits?
    if (fabs(inVal) > 1.0E100) {
        printf("well out of range");
        return 1;
    }

    // load string buffer with input
    char buf[110];
    sprintf(buf, "%0105.0f", inVal);

    // do range check on strings
    if (inVal < 0) {
        char minVal[110];
        sprintf(minVal, "%0105d", INT_MIN);
        if (strcmp(buf, minVal) > 0) {
            printf("too small input: %f\n", inVal);
            return -1;  // needs better error signify
        }
    } else {
        char maxVal[110];
        sprintf(maxVal, "%0105d", INT_MAX);
        if (strcmp(maxVal, buf) < 0) {
            printf("too large input: %f\n", inVal);
            return -1;  // needs better error signify
        }
    }

    // do final conversion
    int result;
    sscanf(buf, "%d", &result);

    printf("input: %f result: %d\n", inVal, result);  // diagnostic

    return result;
}

int main()
{
    // test values    
    convert( 0.);
    convert( -123.5);
    convert( 123.5);

    convert( ((double)INT_MIN)-1);
    convert( ((double)INT_MIN));
    convert( ((double)INT_MIN)+1);
    convert( 2.0*((double)INT_MIN));
    convert( ((double)INT_MIN)/2);

    convert( ((double)INT_MAX)-1);
    convert( ((double)INT_MAX));
    convert( ((double)INT_MAX)+1);
    convert( 2.0*((double)INT_MAX));
    convert( ((double)INT_MAX)/2);

    return 0;
}

这会产生预期的转化(见上文末尾的测试用例):

% gcc test.c ; ./a.out
input: 0.000000 result: 0
input: -123.500000 result: -124
input: 123.500000 result: 124
too small input: -2147483649.000000
input: -2147483648.000000 result: -2147483648
input: -2147483647.000000 result: -2147483647
too small input: -4294967296.000000
input: -1073741824.000000 result: -1073741824
input: 2147483646.000000 result: 2147483646
input: 2147483647.000000 result: 2147483647
too large input: 2147483648.000000
too large input: 4294967294.000000
input: 1073741823.500000 result: 1073741824

【讨论】:

    【解决方案3】:

    也许这可行:

    #define BYTES_TO_BITS(x)    (x*8)
    
    void numToIntnt(double num, int *output) {
        const int upperLimit = ldexp(1.0, (BYTES_TO_BITS(sizeof(int))-1))-1;
        const int lowerLimit = (-1)*ldexp(1.0, (BYTES_TO_BITS(sizeof(int))-1));
    
        /*
         * or a faster approach if the rounding is acceptable:
         * const int upperLimit = ~(1<<(BYTES_TO_BITS(sizeof(int))-1));
         * const int lowerLimit = (1<<(BYTES_TO_BITS(sizeof(int))-1));
         */
    
        if(num > upperLimit) {
            /* report invalid conversion */
        } else if (num < lowerLimit) {
            /* report invalid conversion */
        } else {
            *output = (int)num;
        }
    }                                                                                                                          
    

    【讨论】:

    • 如何返回零值?
    • @AndrewHenle IMO 的目标是实现正确的转换,而不是错误日志,我试图简化它(实际上两个错误都报告相同的值,在一个变量中,其中任何值是期待)。无论如何,我已经编辑了代码以使其更清晰。
    • upperLimit 的计算尝试计算 2^width-1,其中 width 是 int 中的位数。即使其中一些位是填充位,因此它们对可用值没有贡献,减 1 也是一个问题。如果结果不能精确地用浮点表示,C 没有指定会发生什么。它可能会向上或向下舍入。那你就不知道该用val &lt; upperLimit还是val &lt;= upperLimit了。
    • 测试 val &gt; upperLimit 将报告一个 NaN 错误,其他比较也是如此,因此此代码将落入 *err = (int) val 案例,这是我们不想要的。 (为什么叫“err”?这表明错误,但这是为了返回正确的值,不是吗?)这些测试的结构应该使得 if 值在范围内,then 转换,else 报错。然后 NaN 流向错误路径。或者可以单独测试 NaN。
    • 此代码假定最小整数值是 2 的幂的负数,但 C 标准不要求这样做。
    【解决方案4】:

    根本问题是找到min_double_to_intmax_double_to_int,分别是最小和最大的double,可以转换为int

    可移植的转换函数本身在C11中可以写成

    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;
    }
    

    在第一次使用上述函数之前,我们需要分配min_double_to_intmax_double_to_int

    2018-07-03 编辑:重写方法。

    我们可以使用一个简单的函数来找到至少与INT_MAX/INT_MIN 一样大的十的最小幂。如果它们小于DBL_MAX_10_EXP,则double 的范围大于int 的范围,我们可以将INT_MAXINT_MIN 转换为double

    否则,我们构造一个包含INT_MAX/INT_MIN 的十进制表示的字符串,并使用strtod() 将它们转换为double。如果这个操作溢出,说明double的范围小于int的范围,我们可以将DBL_MAX/-DBL_MAX分别作为max_double_to_intmin_double_to_int使用。

    当我们将INT_MAX 作为double 时,我们可以使用循环来增加使用nextafter(value, HUGE_VAL) 的值。使用floor() 向下舍入的有限最大值仍会产生相同的double 值,即max_double_to_int

    类似地,当我们将INT_MIN 作为双精度值时,我们可以使用循环来减少使用nextafter(value, -HUGE_VAL) 的值。仍为有限且向上舍入 (ceil()) 到相同的double 的幅度最大值是min_double_to_int

    下面是一个示例程序来说明这一点:

    #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;
    }
    

    【讨论】:

      【解决方案5】:

      据我所知,基本问题归结为:double->int->double 是 INT_MAX 和 INT_MIN 值的标识。有趣的是,C 有一种表达方式:

      int isok(int val) {
         double dv = val;
         int iv = dv;
         return val == iv;
      }
      

      由此,上述答案的浓缩形式可以起作用,因为您可以使用它来确定 INT_MAX,INT_MIN 是否具有合理的可比性,因此:

      if (isok(INT_MAX) && isok(INT_MIN) && f >= INT_MIN && f < INT_MAX) {
           // do your weirdo float stuff here...
      }
      

      但是,当然,依靠 C 严格的类型转换系统为编译器提供了重新格式化磁盘的免费许可,所以也许可以通过 printf/scanf 来填充它。

      【讨论】:

      • double dv = val; 舍入到更大数量级的情况下,int iv = dv; 是未定义的行为。一个例子是 INT_MAX 用于 64 位 2 的补码 int 和 IEEE 754 双精度 double
      【解决方案6】:

      是的。 (为简洁起见,省略了 nan/inf 处理)

      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;
         }
      }
      

      解释。

      正如其中一个链接答案中所解释的那样,边缘情况是INT_MAX 不能准确表示为double,并且在转换为doubleINT_MIN 的对称情况下向上取整. if (x &gt; INT_MAX) 失败时就是这种情况。即比较返回false,但我们仍然不能直接将x转换为int

      链接的答案未能识别的是只有一个双数未通过测试,即(double)INT_MAX,我们可以通过显式检查x == INT_MAX 轻松捕捉到这种情况。

      编辑 如 cmets 中所述,如果 INT_MAXINT_MIN 超出 double 的范围,这可能会失败。虽然极不可能,但标准并未排除这种情况。在这样的实现中,转换只是(int)x。在配置时检测这样的实现应该比在运行时更容易。如果绝对需要后者,则可以执行此操作一次

      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;
      }
      

      然后

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

      对于我们当中的偏执狂,也可以使用 INT_MIN 执行此操作,并分别执行正负双精度检查。

      【讨论】:

      • 哼。这里的一个问题是 INT_MAX 可能在双精度范围之外。我个人的看法是,C 标准在这种不切实际的可能性上存在缺陷。
      • @Bathsheba 你需要大约 128 位整数,and double 实际上是 IEEE 单精度浮点或更小。这种组合虽然在理论上是可能的,但在热死亡之前不太可能在这个宇宙中发生。
      • @Bathsheba 可以通过DBL_MAX_EXP &gt; sizeof(int)*CHAR_BITS-1 或其他方式检查。
      • 那是火还是冰? (Higg 的玻色子依赖?)在转换问题上,double 值精确表示的最后一个连续整数是9,007,199,254,740,992,它比INT_MAX 高出几个数量级,那么INT_MAX 怎么不能表示为@987654344 @在测试中x &gt; INT_MAX?之前的所有整数值都由 double 精确表示。
      • @DavidC.Rankin C 标准不强制要求这些数字中的任何一个。 INT_MAX 没有上限。 OTOH double 允许与 float 一样小。
      【解决方案7】:

      doubleint 的转换可以用可移植的C 编写吗(?)

      有没有办法跨平台实现double_to_int函数(仅基于C标准,甚至不考虑目标平台支持IEEE-754)。?

      int double_to_int(double val, int *err)
      

      详细信息:(int)val 截断小数部分,因此使用(int)val 的可转换val 的范围在数学上为:
      INT_MIN - 0.9999... ≤ val ≤ INT_MAX + 0.9999...
      INT_MIN - 1 &lt; val &lt; INT_MAX + 1


      是的一种跨平台方式,通过使用精确的浮点数学和常量,代码可以测试转换是否成功。

      2.0*(INT_MAX/2+1) 肯定会完全转换为 FP 常量。

      val - INT_MIN &gt; -1.0 类似于val &gt; INT_MIN - 1.0,但不会遭受INT_MIN - 1.0 可能出现的不精确性(使用常见的 2 的补码机器)。回想一下,整数类型可能比double 具有更高的精度。考虑一个 64 位的 intINT_MIN - 1.0 不能完全表示为 double

      代码不使用(double)INT_MAX,这也可能不精确。


      复制myself:

      #include <limits.h>
      #define DBL_INT_MAXP1 (2.0*(INT_MAX/2+1)) 
      #define DBL_INT_MINM1 (2.0*(INT_MIN/2-1)) 
      
      int double_to_int(double val, int *err) {
        if (val < DBL_INT_MAXP1) {
          #if -INT_MAX == INT_MIN
          // rare non-2's complement machine 
          if (val > DBL_INT_MINM1) {
            *err = OK;
            return (int) val;
          }
          #else
          if (val - INT_MIN > -1.0) {
            *err = OK;
            return (int) val;
          }
          #endif 
          // Underflow
          *err = ERR_MINUS_INF;
          return INT_MIN;
        }
        if (x > 0) {
          // Overflow
          *err = ERR_PLUS_INF;
          return INT_MAX;
        }
        // NaN;
        *err = ERR_NAN;
        return 0;
      }
      

      角落弱点:FLT == 10 和整数类型 > 34 位。

      【讨论】:

        猜你喜欢
        • 2012-06-03
        • 1970-01-01
        • 2013-05-31
        • 2015-10-07
        • 2014-12-12
        • 2019-11-15
        • 1970-01-01
        • 2014-02-17
        • 1970-01-01
        相关资源
        最近更新 更多