C中两个带符号的32位数字相减时如何检测溢出?

发布于 2024-08-09 01:17:30 字数 212 浏览 6 评论 0 原文

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

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

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

类似的事情。有没有好的方法可以做到这一点?

I have got two signed integers, and I would like to subtract them. I need to know if it overflowed.

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

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

Something like that. Is there a good way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

温暖的光 2024-08-16 01:17:31

首先,有符号计算中的溢出会导致 C 中未定义的行为。

其次,暂时忘记 UB 并坚持 2 补码机的典型溢出行为:溢出是通过结果“移动”到“错误方向”的事实来揭示的从第一个操作数开始,即当结果大于第一个操作数且第二个操作数为正时(或小于第一个操作数且第二个操作数为负)。

在你的情况下

int one, two;

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

Firstly, overflow in signed calculations causes undefined behavior in C.

Secondly, forgetting about UB for a second and sticking to the typical overflow behavior of a 2's complement machine: overflow is revealed by the fact that result "moves" in the "wrong direction" from the first operand, i.e when the result ends up greater than the first operand with positive second operand (or smaller than the first operand with negative second operand).

In your case

int one, two;

int result = two - one;
if ((result < two) != (one > 0))
  printf("overflow");
不…忘初心 2024-08-16 01:17:31

评估ab时需要考虑两种情况:

  1. ab可能下溢,当且仅当b>0 && a<0ab < min == a
  2. ab 可能会溢出,当且仅当 b<0 && a>0ab> max == -b>max-a

这导致我们将 a 情况简化为

#include <limits.h>

int sub_invokes_UB(int a, int b) {
  if ((b > 0) && (a < INT_MIN + b)) return 1; // error
  if ((b < 0) && (a > INT_MAX + b)) return 1; // error
  return 0; // ok
}

There are 2 cases to consider on evaluating a-b:

  1. a-b may underflow, iff b>0 && a<0 and a-b < min == a<min+b
  2. a-b may overflow, iff b<0 && a>0 and a-b > max == -b>max-a

This leads us on simplifying the a case to

#include <limits.h>

int sub_invokes_UB(int a, int b) {
  if ((b > 0) && (a < INT_MIN + b)) return 1; // error
  if ((b < 0) && (a > INT_MAX + b)) return 1; // error
  return 0; // ok
}
谁对谁错谁最难过 2024-08-16 01:17:31

您可以以更高的精度进行比较。假设您有 32 位整数。您可以将它们提升为 64 位整数,进行减法,然后将该结果与其本身转换为 32 位,然后再次转换为 64 位进行比较。

我不会用 int 这样做,因为该语言不能保证大小......也许 int32_tint64_t来自 (来自 C99)。

如果您使用的是 Windows,可以使用 ULongSub () 等,溢出时返回错误代码。

You can do it with higher precision and compare. Say you have 32-bit integers. You can promote them to 64-bit integers, subtract, then compare that result with itself cast to 32-bit and then up again to 64 bits.

I wouldn't do it this way with int, because the language doesn't give you guarantees on sizes... Maybe int32_t and int64_t from <inttypes.h> (from C99).

If you're on Windows use can use ULongSub() etc, which returns an error code on overflow.

故事还在继续 2024-08-16 01:17:31

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
}

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
}
网白 2024-08-16 01:17:30

您需要在溢出(或下溢)发生之前捕获它。一旦发生这种情况,您就处于未定义行为境地,所有的赌注都会被取消。

#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;
}

You need to catch the overlow (or underflow) before it happens. Once it happens you're in Undefined Behaviour land and all bets are off.

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文