我有两个数字:A
和B
。我需要A+B
在我的代码中的某个地方进行计算。两者都是A
,它们可以是正面的或负面的。B
long long
我的代码运行错误,我怀疑在计算A+B
. 我只是想检查是否A+B
超出long long
范围。所以,任何方法都是可以接受的,因为我只用它来调试。
只有当两个数字具有相同的符号时,才可能发生溢出。如果两者都是正数,那么如果在数学A + B > LLONG_MAX
上或等价上,你就会溢出B > LLONG_MAX - A
。由于右手边是非负的,后一个条件已经暗示B > 0
。类似的论点表明,对于否定的情况,我们也不需要检查符号B
(感谢Ben Voigt指出符号检查B
是不必要的)。然后你可以检查
if (A > 0) {
return B > (LLONG_MAX - A);
}
if (A < 0) {
return B < (LLONG_MIN - A);
}
return false;
检测溢出。由于初始检查,这些计算不会溢出。
检查结果的符号A + B
将与溢出整数计算的保证环绕语义一起工作。但是有符号整数的溢出是未定义的行为,即使在实现了回绕行为的 CPU 上,编译器也可能假设没有发生未定义的行为,并在实现时完全删除溢出检查。因此,对该问题的评论中建议的检查非常不可靠。
类似于以下内容:
long long max = std::numeric_limits<long long>::max();
long long min = std::numeric_limits<long long>::min();
if(A < 0 && B < 0)
return B < min - A;
if(A > 0 && B > 0)
return B > max - A;
return false;
我们可以这样推理:
如果A
和B
是相反的符号,它们就不能溢出——大于零的一个需要大于max
或小于零的一个需要小于min
。
在其他情况下,简单的代数就足够了。A + B > max => B > max - A
如果它们都是正面的,则会溢出。否则,如果它们都是负数,A + B < min => B < min - A
。
此外,如果您仅将其用于调试,则可以使用以下“hack”直接从最后一个操作中读取溢出位(假设您的编译器/cpu 支持此):
int flags;
_asm {
pushf // push flag register on the stack
pop flags // read the value from the stack
}
if (flags & 0x0800) // bit 11 - overflow
...
屏蔽符号,转换为无符号值,然后执行加法。如果它在上面,1 << (sizeof(int) * 8 - 1)
那么你有一个溢出。
int x, y;
if (sign(x) == sign(y)){
unsigned int ux = abs(x), uy = abs(y);
overflow = ux + uy >= (1 << (sizeof(int) * 8 - 1));
}
更好的是,让我们编写一个模板:
template <typename T>
bool overflow(signed T x, signed T y){
unsigned T ux = x, uy = y;
return ( sign(x) == sign(y) && (ux + uy >= (1 << (sizeof(T) * 8 - 1)));
}