整数除法溢出

Integer division overflows

问题

我一直在考虑整数(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. 可以编写什么(跨平台)C 代码来防止除法溢出(对于类型(有符号)int)?

  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) 与 C 中的任何其他操作一样,应用程序必须确保:

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

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

2) 如果您使用 C 标准的 stdint.h,您可以保证变量有多大,可移植。编写可移植代码时永远不要使用 int

对于写安全除法例程的情况,是以32位整数为参数,然后对64位整数进行计算,return结果为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 替换为枚举。

What guarantees (in C or C++ standard) might help to devise the code?

C 将有符号整数表示指定为使用 3 种形式中的一种:符号和大小、二进制补码或个数补码。给定这些形式,只有除以 0 和 INT_MIN/-1 的二进制补码除法可能会溢出。

What (cross-platform) C code could one write in order to prevent division overflows (for type (signed) int)?

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