Murph
Murph

Reputation: 1509

How to detect overflow when subtracting two signed 32 bit numbers in C?

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?

Upvotes: 5

Views: 13463

Answers (5)

Jay-Pi
Jay-Pi

Reputation: 428

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
}

Upvotes: 1

less_sleep
less_sleep

Reputation: 1

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
}

Upvotes: -1

pmg
pmg

Reputation: 108986

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

Upvotes: 15

AnT stands with Russia
AnT stands with Russia

Reputation: 320747

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");

Upvotes: 7

asveikau
asveikau

Reputation: 40264

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.

Upvotes: 0

Related Questions