累计和溢出

时间:2018-08-06 18:15:34

标签: c overflow

假设我要加1 + 11 + 111 ....加n次。 显然,从证明素值n开始,累积总和可能会溢出。

假设我使用以下非常简单的函数来计算上述总和:

int calcSum(int num)
{
    int sum = 0, sequnt = 1, i;

    for (i = 0; i < num; i++)
    {
        sum += sequnt;
        sequnt = (sequnt * 10) + 1;
    }

    return sum;
}

对于该功能,我想添加一个检查溢出的地方。

我试图在这里How to check if a number overflows an 'int'

寻求帮助。

但是我不得不承认它使我感到困惑,并且在执行任务中仍然遇到一些困难。

任何帮助都会得到补偿。

3 个答案:

答案 0 :(得分:3)

只需使用INT_MAX中的limits.h

int calcSum(int num)
{
    int sum = 0, sequnt = 1, i;

    for (i = 0; i < num; i++)
    {
        if (INT_MAX - sequnt < sum) exit(1); // overflow
        sum += sequnt;
        if (INT_MAX/10 <= sequnt) exit(1); // overflow on the two next sentences.
        sequnt *= 10;
        sequnt++;
    }

    return sum;
}

exit(1)仅是为了使示例简短。您可以添加所需的任何错误处理。

答案 1 :(得分:3)

由于2个加法或乘法中的任意一个都是溢出候选,因此请为每个安全调用overflow checker。在<limits.h>中使用常量来指导范围检查。

#include <limits.h>
int is_undefined_add(int a, int b) {
  return (a < 0) ? (b < INT_MIN - a) : (b > INT_MAX - a);
}

int is_undefined_mult(int a, int b) {
  if (a > 0) {
    if (b > 0) {
      return a > INT_MAX / b;       // a positive, b positive
    }
    return b < INT_MIN / a;         // a positive, b not positive
  }
  if (b > 0) {
    return a < INT_MIN / b;         // a not positive, b positive
  }
  return a != 0 && b < INT_MAX / a; // a not positive, b not positive
}

int calcSum(int num) {
  int sum = 0, sequnt = 1, i;

  for (i = 0; i < num; i++) {
    if (is_undefined_add(sum, sequnt) Handle_Overflow();
    sum += sequnt;

    if (is_undefined_mult(sequnt, 10) Handle_Overflow();
    sequnt *= 10;

    if (is_undefined_add(sequnt, 1) Handle_Overflow();
    sequnt++;
  }

  return sum;
}

is_undefined_add()is_undefined_mult()int a, int b的所有组合均有效。

答案 2 :(得分:1)

如果您确定使用的是二进制补码整数,则可以检查以下内容:添加的两个正数必须给出一个正结果,而添加的两个负数必须给出一个负结果。 两次溢出是不可能的,因此,如果您添加正数,则当您的结果首次为负数时就会出现溢出。

无论哪种情况,如果您都希望代码具有可移植性,那么建议您执行类似以下操作:

#include <limits.h>
...
    if ((a > 0 && b > 0 && MAX_INT - a > b) ||
        (a < 0 && b < 0 && MIN_INT - a < b)) {
        /* OVERFLOW OF a + b WILL OCCUR */
        ...
    }

如果操作数的符号不同,则a + b的绝对值不可能大于ab,因此不可能发生溢出。

对于unsigned,您可以采用类似的方法(节省一半的测试时间,因为操作数只能是正数),但是这一次第一种方法始终有效(因为标准表示无符号加法被认为是总和模块2^n,其中n是按字表示的大小,在这种情况下,您可以求和,然后检查结果是否小于任何操作数(如果两个都为正,则求和必须更大)等于或等于任何操作数):

unsigned a, b;

...
unsigned sum = a + b;
if (sum < a || sum < b) {
    /* OVERFLOW ON a + b HAS HAPPENED */
    ...
}

您还必须检查整数乘法溢出。如果ab要相乘,则a*b可能溢出。但是这一次问题更加严重了,因为溢出可能发生不止一次,并且您不能后验。在这种情况下,您可能会用相同或不同符号的溢出,因为如果两个符号相等,则您将ab乘以b(并且if (MAX_INT/b < a) { /* overflow */ } 与它本身具有相同的符号)为正,将发生溢出

if (MIN_INT/b < a) { /* overflow */ }

如果符号不同,则乘积应为负,然后

open layer

如果数字之一为0,则乘法时不会发生溢出,结果为0。