如何检测整数溢出?

我正在用C ++编写一个程序来查找ab = c的所有解,其中a,b和c一起使用所有数字0-9。 程序循环遍历a和b的值,并且每次在a,b和ab上运行一个数字计数例程,以检查数字条件是否满足。

但是,当ab溢出整数限制时,可能会生成伪解。 我结束了检查这个使用代码,如:

unsigned long b, c, c_test;
...
c_test=c*b;         // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test;      // No overflow

有没有更好的溢出测试方法? 我知道某些芯片有一个内部标志,在发生溢出时会被设置,但我从来没有见过通过C或C ++访问它。


有一种方法可以确定操作是否可能溢出,使用操作数中最重要的一位的位置以及一些基本的二进制数学知识。

另外,任何两个操作数都会导致(至多)比最大操作数的最高一位多一位。 例如:

bool addition_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits<32 && b_bits<32);
}

对于乘法,任何两个操作数都会导致(至多)操作数位的总和。 例如:

bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}

同样,你可以估算的结果的最大大小a给力b是这样的:

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(当然,用你的目标整数代替位数。)

我不确定确定数字中最高一位的最快方式,下面是一个强力方法:

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

这并不完美,但是这会给你一个好主意,在做手术之前是否有两个数字可能会溢出。 我不知道这是否会比按照您的建议的方式检查结果更快,因为highestOneBitPosition函数中的循环,但它可能会(特别是如果您知道预先在操作数中有多少位)。


我看到你正在使用无符号整数。 根据定义, 在C中 (不知道C ++),无符号算术不会溢出......所以,至少对于C来说,你的观点是没有意义的:)

使用带符号整数,一旦发生溢出,就会发生未定义行为,并且您的程序可以执行任何操作(例如:渲染测试无结果)。

#include <limits.h>
int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* unreliable test */
  /* ... */
}

要创建一个合格的程序,您需要产生溢出之前测试溢出。 该方法也可以与无符号整数一起使用

// for addition
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;

// for subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;

// for multiplication
#include <limits.h>
int a = <something>;
int x = <something>;
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;
// there may be need to check for -1 for two's complement machines
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */

除了INT_MIN-1特殊情况外,不可能超过INT_MININT_MAX


铿锵3.4+和GCC 5+提供检查算术builtins。 它们为这个问题提供了一个非常快速的解决方案,尤其是与位测试安全检查相比时。

对于OP的问题中的例子,它会这样工作:

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // returned non-zero: there has been an overflow
}
else
{
    // return zero: there hasn't been an overflow
}

Clang文档没有指定如果发生溢出, c_test是否包含溢出结果,但GCC文档声明它确实存在溢出结果。 鉴于这两者喜欢与__builtin兼容,假设Clang的工作方式也可能是安全的。

每种算术运算都有一个__builtin ,用于int大小,长大小和长long大小的溢出(加法,减法,乘法),带有有符号和无符号变量。 名称的语法是__builtin_[us](operation)(l?l?)_overflow

  • u表示无符号或s表示签名;
  • 操作是addsubmul ;
  • 没有l后缀表示操作数是int s; 之一l装置long ; 两个l有什么恶意long long
  • 因此,对于一个有签名的长整数加法,它将是__builtin_saddl_overflow 。 完整列表可以在Clang文档页面找到。

    GCC 5+和锵3.8+另外提供,如果没有指定值的类型工作的通用内建的: __builtin_add_overflow__builtin_sub_overflow__builtin_mul_overflow 。 这些也适用于小于int类型。

    内置版本降低到平台的最佳水平。 在x86上,他们检查进位,溢出和符号标志。

    Visual Studio的cl.exe没有直接的等价物。 为无符号的加法和减法,包括<intrin.h>将允许您使用addcarry_uNNsubborrow_uNN (其中NN是比特数,像addcarry_u8subborrow_u64 )。 他们的签名有点迟钝:

    unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
    unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);
    

    c_in / b_in是输入上的进位/借位标志,返回值是进位/借位输出。 它似乎没有等效于有符号操作或乘法。

    否则,Clang for Windows现在已经可以投入使用(对于Chrome来说足够好),所以这也是一种选择。

    链接地址: http://www.djcxy.com/p/79923.html

    上一篇: How to detect integer overflow?

    下一篇: Does "undefined behaviour" extend to compile