views:

463

answers:

3

I've got two signed integers, and i'd 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?

A: 

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.

asveikau
If I do this case in 32 bit:0x7fffffff - 0x80000000 = 0xffffffffIn 64 bit I get 0xffffffffffffffffand thus i see no overflow (it should be, though. that's the wrong answer).
Murph
forgot to mention that this only really works using unsigned quantities...
asveikau
+7  A: 

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");
AndreyT
If `int result = two - one;` overflows (or underflows) you're in Undefined Behaviour land and anything can happen.
pmg
Which is what stated explicitly in the very first sentence of my response. (Although the term "underflow" in its traditional meaning is not applicable here.)
AndreyT
I like this answer but as you noted - undefined behaviour. If is safe to assume that the result will 'move' in the 'wrong direction'?
Kirk Broadhurst
My point is that your `if` is not reliable. After the operation on the line before, if there was overflow, the result of the comparison is meaningless.
pmg
Well, I'm doing this under *assumption* (which I know is just an assumption) that this particular kind of UB typically manifests itself in practice as implementation-defined behavior and that a typical implementation on 2's complement machine "defines" it the way I expect it to be defined. I wouldn't use this trick myself on signed types (it is perfectly well defined on unsigned ones, BTW) in "functional" code, but rather restrict it to "assertion" code, i.e use it as a mere sanity check. As a sanity check this code is perfectly fine.
AndreyT
@pmg: Think of it as a solution specifically intended for implementations that *extend* C language specification by defining the signed integer overflow behavior in the way implied in my answer.
AndreyT
LOL ok. Assuming the behaviour is implementation-defined your answer is good ... for that implementation :)
pmg
+2  A: 

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