views:

272

answers:

6

I am attempting to determine if I can compute the sum of two 32 bit integers without overflow, while making use of only certain bitwise and other operators. So, if the integers x and y can be added without overflow, the following code should return 1, and 0 otherwise.

(((((x >> 31) + (y >> 31)) & 2) >> 1))

However, it returns 0 when it should be 1 and vice versa. When I employ the logical NOT (!) operator, or bitwise XOR (^) with 0x1, it does not fix the issue.

!(((((x >> 31) + (y >> 31)) & 2) >> 1))

(((((x >> 31) + (y >> 31)) & 2) >> 1) ^ 0x1)

^ these don't work.

Thanks in advance.

+8  A: 

This is a bit cleaner:

~(x & y) >> 31

Update

kriss' comment is correct. all this code does is check that the two MSBs are both set.

I was just looking at kriss' answer, and it occurred to me that the same thing can be done using only a single addition, plus bitwise operators, assuming unsigned ints.

((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)

The first parenthesised section sets both MSB to 0 then adds the result. Any carry will end up in the MSB of the result. The next bitmask isolates that carry. The final term checks for a set MSB on either x or y, which result in a carry overall. To meet the spec in the question, just do:

~(((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)) >> 31
Andrew Cooper
@Andrew Cooper: Well, it's shorter and cleaner than the OP's code, but has the same flaw. It does not check for possible overflow at all, just that both numbers are large unsigned integers. What about `0xF0000000+0x7F000000` ? Looks like unnoticed overflow.
kriss
True. I was just trying to replicate the effect of Rowhawn's code. I didn't even think about what it was doing. Thinking about it now I'm not sure it's feasible to do what he wants. Take the extreme example of 0xFFFFFFFF+0x1. That will overflow but it's hard to check using bitwise operators. The simplest thing may be to just do the addition and check for overflow.
Andrew Cooper
+1  A: 

The logical ! is working fine for me.

me@desktop:~$ cat > so.c
#include <stdio.h>

void main() {
    int y = 5;
    int x = 3;
    int t;
    t = (((((x >> 31) + (y >> 31)) & 2) >> 1));
    printf("%d\n", t);
    t = !(((((x >> 31) + (y >> 31)) & 2) >> 1));
    printf("%d\n", t);
}
^D
me@desktop:~$ gcc -o so so.c
me@desktop:~$ ./so
0
1
me@desktop:~$ uname -a
Linux desktop 2.6.32-23-generic #37-Ubuntu SMP Fri Jun 11 07:54:58 UTC 2010 i686 GNU/Linux
JoelFan
A: 

All of these answers are correct, I suspect something is wrong with the code I'm using to test my function, because no matter what I do, I get 0 when I want 1 and 1 when I want 0

Rowhawn
@Rowhawn: you should not put that in answers, but in comments, or as edit to the original question. And if you want us to check how you test you function, just post code.
kriss
A: 

Are those signed integers by any chance? Your logic looks like it should be fine for unsigned integers (unsigned int) but not for regular ints, since in that case the shift will preserve the sign bit.

davmac
yes, they are signed integers, sorry i forgot to specify
Rowhawn
+1  A: 

Let's suppose both numbers are unsigned integers. If you work with signed integers, it would be a little be more tricky as there is two ways to get overflow, either adding two large positives of adding two large negative. Anyway checking the most significant bits won't be enough, as addition propagates carry bit, you must take it into account.

For unsigned integers, if you don't care to cheat an easy way is:

 (x+y < x) || (x+y < y)

This will work as most compilers won't do anything when overflow happen, just let it be.

You can also remarks that for overflow to happen at least one of the two numbers must have it's most significant bit set at 1. Hence something like that should work (beware, untested), but it's way more compilcated than the other version.

/* both Most Significant bits are 1 */
(x&y&0x80000000)        
/* x MSb is 1 and carry propagate */
 ||((x&0x80000000)&&(((x&0x7FFFFFFF)+y)&0x80000000))
/* y MSb is 1 and carry propagate */
 ||((y&0x80000000)&&(((y&0x7FFFFFFF)+x)&0x80000000))
kriss
Your first code is guaranteed to work, as unsigned numbers operate under modulo.
GMan
@GMan: nice to know. Is it in standard ? Still thought some rare CPU raised hardware exceptions when overflow occurred and that it was allowed by standard. But I may be wrong.
kriss
This code can be simplified to a single addition + bitwise ops. See my updated answer.
Andrew Cooper
GMan is correct; kriss is mistaken. Hardware exceptions are only allowed for **signed** integer overflow. Unsigned arithmetic never overflows; it wraps modulo `UINT_MAX+1` (or whatever the right value for the type is) according to the specification, and always has done so.
R..
See my answer for a simpler, more correct test for unsigned integers.
R..
@R..: well, some processors set a carry flag for unsigned integer overflow and an overflow flag for internal overflow to most significant bit. For such processors raising some error is as easy for signed as for unsigned. If this is indeed so, this is yet another suprising choice from C stndard comittee that looks mostly arbitrary. Anyway, I know of no compiler that raise exception in such case that being for signed or unsigned.
kriss
@kriss: Yes, a compiler must not do anything special when an unsigned integral value overflows. Just needs to wrap.
GMan
+1  A: 

There is no simple bit-arithmetic-based test for overflow because addition involves carry. But there are simple tests for overflow that do not involve invoking overflow or unsigned integer wrapping, and they're even simpler than doing the addition then checking for overflow (which is of course undefined behavior for signed integers):

For unsigned integers x and y: (x<=UINT_MAX-y)

For signed integers, first check if they have opposite signs. If so, addition is automatically safe. If they're both positive, use (x<=INT_MAX-y). If they're both negative, use (x>=INT_MIN-y).

R..