views:

267

answers:

3

Comparing two numbers x,y; if x>y I return 1, else return 0. I am trying to do this using only bitwise operators such as >> << ~ ! & | ^ +.

So far I got this:

int greaterThan(int x,int y) {
return ((~(x-y))>>31);
}

So if x-y is negative, the most significant bit would be a 1. So I negate it to get a 0 and shift it so that it returns a 0. If x-y is positive, the most significant bit would be 0. Negate it to get 1 and shift it to return 1. It doesn't seem to work. Am I doing this wrong or missing something?

+2  A: 

Your method does not work for several reasons:

  • Assuming x and y are signed values, the subtraction could overflow. Not only does this result in undefined behavior according to the C standard, but on typical implementations where overflow wraps, it will give the wrong results. For instance, if x is INT_MIN and y is 1, x-y will yield INT_MAX, which does not have its high bit set.

  • If x and y are unsigned values, then x=UINT_MAX and y=0 is an example where you get the wrong answer. You'd have to impose a condition on the values of x and y (for instance, that neither has its high bit set) in order for this test to work.

What it comes down to is that in order to perform comparisons by testing the "high bit", you need one more bit than the number of bits in the values being compared. In assembly language on reasonably-CISC-ish architectures, the carry flag provides this extra bit, and special conditional jump instructions (as well as carry/borrow instructions) are able to read the value of the carry flag. C provides no way to access such a carry flag, however. One alternate approach might be to use a larger integer type (like long long) so that you can get an extra bit in your result. Good compilers will translate this to a read of the carry flag, rather than actually performing larger arithmetic operations.

R..
i forgot to mention I am working on 2's complement. I'm working with a lot of restriction on this. I can only use ints. If I can check there is a overflow, is there another way to check that the x>y?
Dan
@R..: Integer overflow does *not* result in undefined behaviour. The behaviour of integer over-/underflows is well-defined for each host platform.
DevSolar
@DevSolar, I think R is correct. See [CERT](https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow) and §6.5/5 of C99. Of course, *unsigned* overflow is guaranteed to wrap.
Matthew Flaschen
"Of course, *unsigned* overflow is guaranteed to wrap." - since no CPU I know of distinguishes between signed or unsigned integers on the machine-code level, the only thing undefined in this case is the *representation* / *interpretation* of negative numbers, which is well-defined by the platform / CPU. I admit this is a corner case, but I feel the statement "this does result in undefined behaviour" is a tad strong here.
DevSolar
It's not overly strong. Sure the machines with non-twos-complement arithmetic are legacy crap, but gcc purposefully exploits the fact that signed overflow is UB to better optimize. Hint: signed overflow being UB means gcc can always assume an additional constraint, which it would otherwise have a hard time proving, that the values used in arithmetic are within a range where overflow would not happen.
R..
+1  A: 

C Standard (1999), chapter 6.5.7 Bitwise shift operators, paragraph 5:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined.

I.e., the result for a bit-shift-right on negative numbers is implementation-defined. One possibility is that the "sign bit" gets copied into the bits to the right, which is what GCC seems to do (as it results in all bits set, -1).

DevSolar
That's correct, but shouldn't cause Dan's incorrect results, since 1 and -1 (in fact all non-zero numbers) have the same truth value in C. The only issue would be if he did something like `if(res == 1)`.
Matthew Flaschen
@Matt, I can't use any if/else statements or ==. Here are some more failed test. Test (1,-2147483648) failed.Test (1,-3) failed.Test (1,-1299006977) failed.Test (1,-2147483647) failed.Test (1,-2) failed.Test (1,-707712875) failed.Test (1,-2147483646) failed.Test (1,-1) failed.
Dan
@Dan, I tried the version with the `greaterThan` function, and it still works fine as long as there's no overflow. So (1,-3), (1,-2), (1,-1) are all -1, which is non-zero (true). What is `greaterThan` returning for you for those pairs?
Matthew Flaschen
Its returning -1 as you said, but I am suppose to return only 0 or 1. So in the case of an overflow, how would I carry it out?
Dan
A: 

@Dan, can you please clarify your question in first post, i.e. what exactly you can use and what you can't use (in your comments you mentioned that you can't use if/else etc.).

Anyway, if you want to get rid of unexpected -1, you should consider casting your value to unsigned int before shifting. This will not make your solution entirely correct though. Try to think about checking numbers' sign bit (e.g. if first bit of x is 0 and first bit of y is 1 then you can be sure that x>y).

ecik