整数除法溢出

20

问题

我一直在思考整数类型(int)溢出的问题,我发现除法也可能会导致溢出。

示例:在我的当前平台上,我有以下代码:

INT_MIN == -INT_MAX - 1

因此

INT_MIN < -INT_MAX

因此

INT_MIN / -1 > -INT_MAX / -1

因此

INT_MIN / -1 > INT_MAX.
因此,除法 ( INT_MIN / -1 ) 会溢出。

问题

因此,我有两个问题:

  1. 为了防止 (signed) int 类型的除法溢出,人们可以编写什么(跨平台)C代码?

  2. C或C ++标准中有哪些保证可以帮助设计代码?


例如,如果标准保证我们要么有

INT_MIN == -INT_MAX - 1
或者
INT_MIN == -INT_MAX,

然后出现了以下代码以防止溢出。

#include <limits.h>

/*
      Try to divide integer op1 by op2.
      Return
        0 (success) or
        1 (possibly overflow prevented).
      In case of success, write the quotient to res.
*/

int safe_int_div(int * res, int op1, int op2) {

  /*   assert(res != NULL);   */
  /*   assert(op2 != 0);      */

  if ( op1 == INT_MIN && op2 == -1 )  {
    return 1;
  }
  *res = op1 / op2;
  return 0;
}

根据惯例,出现错误时应返回“-1”。 - alk
1
@Anon,如果你添加类似于if (!op2) return 2的内容,你就可以解决另一个除法问题 :) - Sir Jo Black
它与计算机在硬件中执行除法的方式有关。 - ThunderWiring
2个回答

19
哪些保证(在C或C++标准中)可以帮助设计代码?
C指定有3种形式的带符号整数表示:符号和大小,二进制补码或一的补码。在这些形式中,只有除以0和二进制补码除法的INT_MIN / -1可能会溢出。
更新:2023年:预计C23仅支持二进制补码。
为了防止类型(有符号)int的除法溢出,一个人能写什么样的跨平台C代码?
int safe_int_div(int * res, int op1, int op2) {
  if (op2 == 0) {
    return 1;
  }
  // 2's complement detection
  #if (INT_MIN != -INT_MAX) 
    if (op1 == INT_MIN && op2 == -1)  {
      return 1;
    }
  #endif
  *res = op1 / op2;
  return 0;
}

1
值得一提的是,如果您尝试在没有上述保护措施的情况下除以 INT_MIN/-1,可能会导致硬件(CPU架构、FPGA或某些RTL)停止运行。 - 0x90
或者在底层内核/固件没有正确处理异常的情况下。 - 0x90

1

1) 就像在C语言中的任何其他操作一样,应用程序必须确保:

  • 用于计算本身的类型足够大,并且
  • 存储结果的变量的类型足够大。

确保这一点的方法是在操作之前设置每个操作数的大小限制。适当的限制取决于算法和变量的目的。

2) 如果使用C标准库中的stdint.h,则可以获得可移植性的变量大小保证。编写可移植代码时永远不应使用int

至于编写安全的除法例程的情况,它将使用32位整数作为参数,然后在64位整数上执行计算并将结果作为32位整数返回。

#include <stdint.h>
#include <stdbool.h>

/*
      Try to divide integer op1 by op2.
      Return
        true (success) or
        false (possibly overflow prevented).
      In case of success, write the quotient to res.
      In case of failure, res remains untouched.
*/

bool safe_int_div (int32_t* res, int32_t op1, int32_t op2) {

  if(op2 == 0)
    return false;

  int64_t res64 = (int64_t)op1 / (int64_t)op2;

  if(res64 > INT32_MAX || res64 < INT32_MIN)
    return false;

  *res = (int32_t)res64_t;

  return true;
}

如果需要更多关于为什么该分割失败的信息,请用枚举替换 bool。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接