在C中减去两个有符号的32位数时如何检测溢出?

bwntbbo3  于 2023-04-11  发布在  其他
关注(0)|答案(5)|浏览(163)

我有两个有符号的整数,我想把它们相减,我需要知道它是否溢出了。

int one;
int two;
int result = two-one;

if (OVERFLOW) {
    printf("overflow");
} else {
    printf("no overflow");
}

差不多吧。有什么好办法吗?

kyxcudwk

kyxcudwk1#

你需要在溢出(或下溢)发生之前抓住它。一旦它发生,你就进入了“未定义行为”的状态,所有的赌注都被取消了。

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

int sum_invokes_UB(int a, int b) {
  int ub = 0;
  if ((b < 0) && (a < INT_MIN - b)) ub = 1;
  if ((b > 0) && (a > INT_MAX - b)) ub = 1;
  return ub;
}

int main(void) {
  printf("(INT_MAX-10) + 8: %d\n", sum_invokes_UB(INT_MAX - 10, 8));
  printf("(INT_MAX-10) + 100: %d\n", sum_invokes_UB(INT_MAX - 10, 100));
  printf("(INT_MAX-10) + INT_MIN: %d\n", sum_invokes_UB(INT_MAX - 10, INT_MIN));
  printf("100 + INT_MIN: %d\n", sum_invokes_UB(100, INT_MIN));
  printf("-100 + INT_MIN: %d\n", sum_invokes_UB(-100, INT_MIN));
  printf("INT_MIN - 100: %d\n", sum_invokes_UB(INT_MIN, -100));
  return 0;
}
eh57zj3b

eh57zj3b2#

首先,有符号计算中的溢出导致C中的未定义行为。
其次,暂时忘记UB,坚持2的补码机器的典型溢出行为:溢出由结果从第一操作数在“错误方向”上“移动”的事实揭示,即,当结果最终大于第一操作数而具有正的第二操作数(或小于第一操作数而具有负的第二操作数)时。
就你的情况而言

int one, two;

int result = two - one;
if ((result < two) != (one > 0))
  printf("overflow");
8ulbf1ek

8ulbf1ek3#

在评估a-b时需要考虑两种情况:

  1. a-b可能下溢,当且仅当b>0 && a<0a-b < min == a<min+b
  2. a-b可能溢出,当且仅当b<0 && a>0a-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
}
u1ehiz5o

u1ehiz5o4#

你可以用更高的精度和比较来做,比如说你有一个32位的整数,你可以把它们提升到64位的整数,相减,然后把结果和它本身进行比较,然后再转换到32位,再转换到64位。
对于int,我不会这样做,因为语言不能保证大小......也许int32_tint64_t来自<inttypes.h>(来自C99)。
如果你在Windows上使用,可以使用[ULongSub()](http://msdn.microsoft.com/en-us/library/bb776671(VS.85).aspx)等,它在溢出时返回一个错误代码。

k10s72fa

k10s72fa5#

https://llvm.org/doxygen/MathExtras_8h_source.html

/// 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
}

相关问题