a-b可能溢出,当且仅当b<0 && a>0和a-b > max == -b>max-a 这导致我们将a案例简化为
#include <limits.h>
int sub_invokes_UB(int a, int b) {
if ((b > 0) && (a < INT_MIN + b)) ub = 1; // error
if ((b < 0) && (a > INT_MAX + b)) ub = 1; // error
return 0; // ok
}
/// Subtract two signed integers, computing the two's complement truncated
/// result, returning true if an overflow ocurred.
template <typename T>
std::enable_if_t<std::is_signed<T>::value, T> SubOverflow(T X, T Y, T &Result) {
#if __has_builtin(__builtin_sub_overflow)
return __builtin_sub_overflow(X, Y, &Result);
#else
// Perform the unsigned addition.
using U = std::make_unsigned_t<T>;
const U UX = static_cast<U>(X);
const U UY = static_cast<U>(Y);
const U UResult = UX - UY;
// Convert to signed.
Result = static_cast<T>(UResult);
// Subtracting a positive number from a negative results in a negative number.
if (X <= 0 && Y > 0)
return Result >= 0;
// Subtracting a negative number from a positive results in a positive number.
if (X >= 0 && Y < 0)
return Result <= 0;
return false;
#endif
}
5条答案
按热度按时间kyxcudwk1#
你需要在溢出(或下溢)发生之前抓住它。一旦它发生,你就进入了“未定义行为”的状态,所有的赌注都被取消了。
eh57zj3b2#
首先,有符号计算中的溢出导致C中的未定义行为。
其次,暂时忘记UB,坚持2的补码机器的典型溢出行为:溢出由结果从第一操作数在“错误方向”上“移动”的事实揭示,即,当结果最终大于第一操作数而具有正的第二操作数(或小于第一操作数而具有负的第二操作数)时。
就你的情况而言
8ulbf1ek3#
在评估
a-b
时需要考虑两种情况:a-b
可能下溢,当且仅当b>0 && a<0
和a-b < min == a<min+b
a-b
可能溢出,当且仅当b<0 && a>0
和a-b > max == -b>max-a
这导致我们将
a
案例简化为u1ehiz5o4#
你可以用更高的精度和比较来做,比如说你有一个32位的整数,你可以把它们提升到64位的整数,相减,然后把结果和它本身进行比较,然后再转换到32位,再转换到64位。
对于
int
,我不会这样做,因为语言不能保证大小......也许int32_t
和int64_t
来自<inttypes.h>
(来自C99)。如果你在Windows上使用,可以使用
[ULongSub()](http://msdn.microsoft.com/en-us/library/bb776671(VS.85).aspx)
等,它在溢出时返回一个错误代码。k10s72fa5#
https://llvm.org/doxygen/MathExtras_8h_source.html