如何检测整数溢出?
我正在用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_MIN
或INT_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
表示签名; add
, sub
或mul
; 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_uNN
和subborrow_uNN
(其中NN是比特数,像addcarry_u8
或subborrow_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