12

As part of my CS classes I've recently completed the pretty popular "Data Lab" assignments. In these assignments you are supposed to implement simple binary operations in C with as few operations as possible.

For those who are not familiar with the "Data Lab" a quick overview about the rules:

  • You may not call functions, cast or use control structures (e.g. if)
  • You may assign variables with no operator cost, however only int is allowed)
  • The less operators you use, the better
  • You may assume sizeof(int) == 32
  • Negative numbers are represented in 2's complement

The task is to implement a logical not called 'bang' (where bang(x) returns !x) by only using the following operators: ~ & ^ | + << >>

The function prototype is defined as

int bang(int x)

The best implementation I could find (using 5 operators) was the following:

return ((x | (~x +1)) >> 31) + 1

However there seems to be a way to accomplish this with even less operators, since I found a result website[1] from some German university where two people apparently found a solution with less than 5 operator. But I can't seem to figure out how they accomplished that.

[1] http://rtsys.informatik.uni-kiel.de/~rt-teach/ss09/v-sysinf2/dlcontest.html (logicalNeg column)

To clarify: This is not about how to solve the issue, but how to solve it with less operations.

ntldr
  • 143
  • 3
  • 8
  • 2
    possible duplicate of [Check if a number is non zero using bitwise operators in C](http://stackoverflow.com/questions/3912112/check-if-a-number-is-non-zero-using-bitwise-operators-in-c) – Keith Nicholas May 06 '15 at 23:16
  • 1
    Not really a duplicate since I already know how to accomplish that. I'm seeking to figure out how to achieve that in less operations. – ntldr May 06 '15 at 23:23
  • 1
    thing is, the question has actually been asked multiple times now, really don't need another version of it – Keith Nicholas May 06 '15 at 23:41
  • 1
    I can't see where the question of doing this in a simpler way was asked or answered at all. Again, this is not an issue on solving this problem but more of a question about the more simple solution (which apparently exists). – ntldr May 06 '15 at 23:47
  • 1
    You get +, but not -? And you don't have logical right-shift? – user2357112 May 06 '15 at 23:52
  • 1
    Yes. - can be expressed as ~x + 1 since we assume negative numbers to be represented as 2's complement. – ntldr May 06 '15 at 23:56
  • 1
    Have you tried `( x ^ 0xFFFFFFFF )` ? – Chan Kha Vu May 07 '15 at 00:14
  • 1
    @FalconUA: That's `~`, not `!`. `~` is already in the allowed operator list. – user2357112 May 07 '15 at 00:19

3 Answers3

5

Only slightly cheating:

int bang(int x) {
    return ((x ^ 0xffffffffU) + 1UL) >> 32;
}

is the only way I can think of to do it in only 3 operations. Assumes a 32-bit int and 64-bit long...

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226
3

If you take the liberty of assuming that int addition overflow is well-defined and wraps (rather than being undefined behavior), then there's a solution with four operators:

((a | (a + 0x7fffffff)) >> 31) + 1

I think you are assuming that overflow is defined to wrap otherwise your function ((x | (~x + 1)) >> 31) + 1 has undefined behavior for x=INT_MIN.

Paul Hankin
  • 54,811
  • 11
  • 92
  • 118
2

why not just :-

int bang(int x)
{
    return 1 >> x;
}
Keith Nicholas
  • 43,549
  • 15
  • 93
  • 156
  • 1
    The instructions for the linked assignment say "You may assume that your machine... Has unpredictable behavior when shifting an integer by more than the word size." Still, it's possible the guy who got a 3-op score might have used a similar idea. – user2357112 May 07 '15 at 00:26
  • 1
    @user2357112: I'm not seeing a "linked assignment", only a link to a table of results. Can you (or someone) clue me in? – Michael Burr May 07 '15 at 00:34
  • 1
    @user2357112 Likewise not seeing a linked assignment, did you look it up? Anyway, of course shifting an `int` by greater than the width of an `int` is also UB (see [6.5.7.3](http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf)) – Iskar Jarak May 07 '15 at 00:35
  • 1
    @MichaelBurr: I took a look through the [folder](http://rtsys.informatik.uni-kiel.de/~rt-teach/ss09/v-sysinf2/) the results page was in and found the assignment. – user2357112 May 07 '15 at 00:36
  • 1
    `1 >> (x >> (x >> 31))` takes care of negative `x`, but doesn't work when `x` is a multiple of 32. – dddsnn May 07 '15 at 02:21
  • 2
    @dddsnn right shifting a negative number CAN shift in 1s to keep the number negative so -4 >> 1 == -2 – Keith Nicholas May 07 '15 at 02:53
  • 1
    If you want to get rid of the negative, `int mask = x >> 31; return 1 >> ((x + mask) ^ mask);` will work. That is 4 ops, and it still leaves you right shifting by more than a word width. Typical machine behaviour is either to shift by the word width (which will have the desired effect) or to treat it as a shift by zero (which will not). – Iskar Jarak May 07 '15 at 03:17
  • Oh, and of course `1 >> (x & 0x7FFFFFFF);` (clearing the sign bit, which is only 2 ops). Both methods will fail on `INT_MIN`, though, since it cannot be represented as a positive integer of the same word size. – Iskar Jarak May 07 '15 at 04:11