整数除法溢出

时间:2015-05-22 10:16:17

标签: c++ c cross-platform standards integer-overflow

问题

我一直在考虑整数(类型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 ++标准中)可能有助于设计代码?

  3. 例如,如果标准保证我们有

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

2 个答案:

答案 0 :(得分:7)

  

什么保证(在C或C ++标准中)可能有助于设计代码?

C指定有符号整数表示为使用3种形式中的1种:符号和幅度,2的补码或1的补码。给定这些形式,只有0和2的INT_MIN/-1的补码除法可能会溢出。

  

可以编写什么(跨平台)C代码以防止除法溢出(对于类型(带符号)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;
}

答案 1 :(得分:0)

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。