I still hate computers

The problem with checking integer overflows is to remember to do that in the first place, the actual check itself is easy, right? Then it should be an easy task to explain what the following prints, or doesn’t print, and why.

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

int main (void)
{
	int a = INT_MAX;
	int b = a + 42;

	if  (a < 0)                  printf ("a1\n");
	if             (a + 42 < 0)  printf ("a2\n");
	if ((a < 0) || (a + 42 < 0)) printf ("a3\n");
	if  (b < 0)                  printf ("b\n");

	return 0;
}

4 comments ↓

#1 Michiel on 08.20.05 at 1:30 am

Without knowing much about c:

a2
a3
b

Reasons:

a1 = INT_MAX which is > 0, so false
a2 = INT_MAX + 42 which overlows becoming negative ( < 0 ), so true
a3 = false true = true
b = the same as a2

Right?

#2 Simon Budig on 08.20.05 at 12:11 pm

Yes, right, but only for disabled optimisation.

As soon as I enable -O1 -O2 or -O3 I get just a2 and b.
I don’t have much clue on optimizsation, probably the second part of the a3-condition gets omitted, because the first one is a stronger requirement (if a+42<0 then immediately follows that a<0, hence we only need the test for a<0). In a second step the compilier sees that a > 0 and immediately treats the “a<0" test as failed, hence a1 and a3 get omitted.

a2 and b survive, because they have an added level of indirection and actually they even succeed.

That having guessed: I don’t know if this is the way the optimisation works here, but it would explain the result.

Simon.

#3 Tommi Komulainen on 08.22.05 at 5:30 am

Simon, you forgot the short-circuiting behavior which dictates that in a3-condition the (a < 0) needs to be evaluated first.

What I *think* happens is that (a < 0) gets evaluated first, then depending on optimizations (a + 42 < 0) gets reordered to (a < -42) which we can deduce is also false based on the previous condition.

Now, as Owen Taylor pointed out integer overflow is undefined so anything goes…

#4 Simon Budig on 08.22.05 at 7:55 am

I am not sure if the short circuit thing is relevant here: We are talking about optimization at the compile step, while short circuitting happens at runtime. Even when you think about s-c’ing it doesnt change: if the first check (a < 0) fails, then (a + 42 < 0) will fail as well, the second test is actually unnecessary, because it cannot succeed if the first one fails, the result of the or does just depend on the first subexpression.

Of course this reasoning is only valid if you ignore the overflow, but since this behaviour is undefined, we may very well assume that the regular math holds for our optimization and omit the second subexpression.