计算功率:如何测试超过INT_MAX或INT_MIN的值?

时间:2016-07-04 18:07:00

标签: c recursion integer-overflow

我正在尝试实现一个简单的程序来递归计算数字的力量。代码会测试超过INT_MAXINT_MIN的值,并应指定power = -1。但是,即使在一定数量的递归调用之后,当结果变量超过最大值时,它也不会像我希望的那样打印错误消息。

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>

void power(int x, int n, int* result);

int main(int argc, char* argv[])
{
    int x, n, result = 1;
    x = 10;
    n = 20;

    if (n < INT_MIN || x < INT_MIN)
    {
        fprintf(stderr, "Argument(s) out of range\n");
        return 0;
    }

    if (n > INT_MAX || x > INT_MAX)
    {
        fprintf(stderr, "Argument(s) out of range\n");
        return 0;
    }

    if (x != 0)
    {
        power(x, n, &result);
        if (result == -1)
        {
            fprintf(stderr, "Result is out of range\n");
            return 0;
        }
    }

    printf("%d to the power %d = %d\n", x, n, result);
    return 0;
}



void power(int x, int n, int *result)
{
    if (n == 0)
    {
        return;
    }

    if (*result > INT_MAX)
    {
        *result = -1;
        return;
    }

    if (*result < INT_MAX)
    {
        *result = (*result) * x;
        power(x, n - 1, result);
    }       
}

4 个答案:

答案 0 :(得分:2)

(你的倒数第二行代码:)当*result = (*result) * x;返回超过INT_MAX时,它会失败,也许*结果将包含结果%INT_MAX的剩余部分,但也许是一些狂野的东西 - 你永远不会有机会在下一次迭代中测试它​​是否更大。

如果您想测试结果仍然是好的,您需要在实际计算之前检查产品是否超出范围,例如使用:if (*result < INT_MAX/x)。如果这是真的,那么 仍然可以再次繁殖。

答案 1 :(得分:1)

完成乘法后检查溢出为时已晚,但您可以在执行之前进行测试。

*result = (*result) * x;

可以这样做

if(INT_MAX / x < *result) {
    *result = -1;
}
else {
    *result *= x;
}

您的代码必须是正数,因为您使用-1表示溢出(本身是-1的强项)。在这种情况下,使用unsigned int甚至更大的类型可以获得更多的范围。

修改

我已经改变了你的程序,返回结果而不是使用指针参数。它适用于正数。

#include <stdio.h>

int power(int x, int n)
// calculate x power n
{
    int res;
    if (n == 0) {
        return 1;
    }
    res = power(x, n-1);             // recurse
    if(INT_MAX / res < x) {          // check for overflow
        return -1;
    }
    return x * res;
}

int main(void)
{
    int i, n;
    n = 10;
    for(i=0; i<11; i++) {
        printf("%d power %d = %d\n", n, i, power(n, i));
    }
    return 0;
}

节目输出

10 power 0 = 1
10 power 1 = 10
10 power 2 = 100
10 power 3 = 1000
10 power 4 = 10000
10 power 5 = 100000
10 power 6 = 1000000
10 power 7 = 10000000
10 power 8 = 100000000
10 power 9 = 1000000000
10 power 10 = -1

答案 2 :(得分:0)

您只能阻止超过INT_MAX,因为实际上升会导致溢出。除非你能捕获溢出(C不提供任何东西,遗憾的是),你将不得不预防它们。

在乘法之前划分的另一个答案实际上允许你做这种预防。

答案 3 :(得分:0)

第1步改进算法
OP的当前方法需要n次迭代/递归。而不是n使用power(x, n-1);来电,使用log2(n)次来电并每次减半n

递归并不是真的需要。一个简单的循环就可以了。

第2步:测试乘法
在乘法之前,测试潜在的溢出。 Ref

int is_undefined_mult1(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
}

第3步测试特殊输入
示例:提高负功率。

#include <stdbool.h>

// return true on overflow
bool power(int x, int y, int *zp) {
  // Handle negative exponent cases
  if (y < 0) {
    switch (x) {
      case 1:
        *zp = 1;
        return false;
      case -1:
        *zp = y % 2 ? -1 : 1;
        return false;
    }
    return true;
  }
  int z = 1;
  int base = x;
  for (;;) {
    if (y % 2) {
      if (is_undefined_mult1(z, base)) return true;
      z *= base;
    }
    y /= 2;
    if (y == 0) break;
    if (is_undefined_mult1(base, base)) return true;
    base *= base;
  }
  *zp = z;
  return false;
}