2

我得到了一个遗留代码,其中有人不小心将double值分配给了int变量,例如:

int a = 10;
double b = 20;
a = b;

现在要摆脱

警告 C4244:“=”:从“double”转换为“int”,可能丢失数据

警告,我尝试在上游编辑代码并删除不必要的double变量,但结果太乱了!

我也可以使用铸造:

a = (int) b;

但实际上不能保证b会在整数的范围内。我想制作一个辅助功能:

int safeD2I(double inputVar){
  if ((inputVar < INT_MAX) && (INT_MIN < inputVar)){
    return (int) inputVar;
  } else {
    exit(-1);
  }
}

但我不确定这是否是最好的实现。我想知道是否有更规范的方法来处理这个问题?

我想要的是:

  • b变量超出整数范围的情况下,程序立即停止
  • 向终端打印一条错误消息,指示发生此问题的特定行和时间。

提前感谢您的支持。

4

2 回答 2

5

首先,按原样编写代码本身并没有什么错误或不安全的地方。赋值转换是 C 语言中完全合法的部分。如果这是您不知道是否安全完成的遗留代码,您需要进行分析以确定这一点。

如果您发现确实需要捕获可能的越界值(转换为 时会产生未定义的行为(!!) int),那么您的代码既错误又脆弱。比较像:

double x;
...
if (x < INT_MAX) ...

强制INT_MAX键入double以进行比较。实际上,在doubleIEEE 双精度和int32 位的世界中,这恰好是安全的,但例如如果您更改double为32 位是不安全的float,因为 32 位INT_MAX不能用单精度表示float。该值将被四舍五入,然后在四舍五入后进行比较。

现在,事实证明您还有一个错误(<= INT_MAX, not < INT_MAX,是什么是界限)以及不正确的逻辑(||而不是&&),因此四舍五入实际上可以解决部分问题。但依赖它是不对的。相反,您需要制造可以比较的 2 的幂,因此转换为浮点是安全的。例如:

  • 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)

这些都假设INT_MIN实际上是 2 的幂(全范围二进制补码),这是一个合理的现实世界假设,并且是 C2x+ 所要求的。如果你想要更多的通用性,那就需要更多的工作。

最后,我首先写这篇文章是对你的问题的评论,但我越想它,它确实属于一个答案:根据你的 C 熟练程度,我会非常警惕做出任何不必要的或“清理" 对此代码的更改。你可能打破的比你修复的要多得多。仅在您完成分析以确定存在活动错误的地方进行更改,并且不要重构事物或更改要修复的类型。进行简单的直接内联修复。

于 2020-03-22T15:31:12.353 回答
1

将double转换为int的安全方法是什么?

if ((inputVar < INT_MAX) && (INT_MIN < inputVar)){失败的边缘情况。

错误的边缘,因为它更像,但不完全一样 (inputVar < INT_MAX + 1) && (INT_MIN - 1 < inputVar)

请注意代码,some_FP < SOME_INT_MAX因为SOME_INT_MAX可能无法转换为所需的 FP 值,因为整数类型可能比 FP 具有更高的精度。这通常不是int, double.


测试 是否在1double的范围内。注意 () 而不是 []。 (INT_MIN-1 .... INT_MAX+1)

如果不是,则以您选择的某种定义的方式出错或处理。

假设典型的 2 的补码,但不假设double超过的精度int(将代码迁移到这种方式更有用float, long long),一些示例代码:

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

要记录失败的行,请考虑使用宏来传递行号。

int X_int_from_double(double x, unsigned);
#define DOUBLE_TO_INT(x) X_int_from_double((x), __LINE__)

1示例 -2,147,483,648.9999... 到 2,147,483,647.9999...

于 2020-03-22T19:08:04.137 回答