【问题标题】:what is the safe way to convert double to int?将double转换为int的安全方法是什么?
【发布时间】:2020-03-22 15:12:06
【问题描述】:

我收到了一个legacy code,其中有人不小心将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 变量超出整数范围,程序会立即停止
  • 向终端打印一条错误消息,指出发生此问题的特定行和时间。

提前感谢您的支持。

【问题讨论】:

  • 顺便说一句,您的代码将像现在一样传递 any 值。你需要&amp;&amp; 而不是||
  • 如果您正在寻找一种方法来查看是否有任何数据丢失,那么您似乎只是想做作业然后进行比较。例如if( (double)a - b &gt; epsilon) ...
  • 如果索引数组是转换的唯一原因,那么您的状态要好得多。创建一个函数,让您可以访问由 double 索引的数组,将所有 double-to-size_t 转换本地化,并为您的特定数组实现边界检查器,因为 MAX_INT 通常是一个太大的索引,即使它适合int 没有问题。
  • 根据您的 C 熟练程度,我会非常谨慎对此代码进行任何不必要的或“清理”更改。你可能打破的比你修复的要多得多。仅在您已完成分析以确定存在活动错误的地方进行更改,并且不要重构要修复的内容。进行简单的直接内联修复。
  • Can a conversion from double to int be written in portable C。这解决了范围问题,同时避免了未定义的行为,但是,一旦你处理了这个问题,处理分数问题就更容易了。

标签: c error-handling casting safety-critical


【解决方案1】:

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

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

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

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

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

  • if (x &lt; 2.0*(INT_MAX/2+1) &amp;&amp; x &gt;= INT_MIN)
  • if (-x &gt; (-INT_MAX-1) &amp;&amp; x &gt;= INT_MIN)
  • if (-x &gt; INT_MIN &amp;&amp; x &gt;= INT_MIN)

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

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

【讨论】:

  • 谢谢。我现在有点难以理解你的回答。我会研究你所说的,如果我有更多问题,我会回到这里。
  • 请注意,@Foad,即使对于经验丰富的程序员来说,在不改变其行为的情况下修改现有代码的任务是准备要修改的代码的自动化测试。这些应该尽可能彻底,详细涵盖正常、极端和错误情况。可以根据原始代码验证测试,然后根据测试验证修改后的代码。做好这件事既不容易也不容易,但即使在即时代码修订任务完成后,其结果也是对代码库的宝贵补充。
  • @JohnBollinger 测试肯定在计划中。可能会使用谷歌测试。但是现在,我想确保我可以摆脱编译警告。并且不用担心代码。这是一个叉子。我正在开发分支的一个分支上工作。所以如果出了什么问题,我有主保险箱。
  • 为了自己的利益而着手摆脱编译器警告,而不注意代码实际在做什么以及您的更改是否可能会破坏,这无疑是破坏事物的秘诀。这不是编译器警告的用途。
  • x &gt;= INT_MIN 错误地失败 x 值小于 INT_MIN 小于 1.0。当 INT_MIN 是 2 的补码时,x - INT_MIN &gt; -1.0 会覆盖它。
【解决方案2】:

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

if ((inputVar &lt; INT_MAX) &amp;&amp; (INT_MIN &lt; inputVar)){ 未通过边缘情况。

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

小心some_FP &lt; SOME_INT_MAX 之类的代码,因为SOME_INT_MAX 可能无法转换为所需的 FP 值,因为整数类型可能比 FP 具有更高的精度。这通常不是int, double的问题。


测试double是否在(INT_MIN-1 .... INT_MAX+1)的范围内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...

【讨论】:

    猜你喜欢
    • 2011-04-02
    • 1970-01-01
    • 2012-08-16
    • 2015-05-15
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 2023-04-11
    相关资源
    最近更新 更多