1

I am having problem understanding how this piece of code works. I understand when the x is a positive number, actually only (x & ~mark) have a value; but cannot figure what this piece of code is doing when x is a negative number.

e.g. If x is 1100(-4), and mask would be 0001, while ~mask is 1110. The result of ((~x & mask) + (x & ~mask)) is 0001 + 1100 = 1011(-3), I tried hard but cannot figure out what this piece of code is doing, any suggestion is helpful.

/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
  /* mask the sign bit against ~x and vice versa to get highest bit in x. Shift by n-1, and not. */
  int mask = x >> 31;

  return !(((~x & mask) + (x & ~mask)) >> (n + ~0));
}
Floyd
  • 164
  • 2
  • 9
  • Someone else has asked a similar question at http://stackoverflow.com/questions/14792521/bitwise-operations-and-shifts – McLovin Jul 10 '14 at 02:22
  • Yeh I have read and understand that way, but just curious how this one works – Floyd Jul 10 '14 at 02:28
  • Right-shifting negative number will add 1 to the MSB (left most bit), if `x` is -4 then `mask` would be -1 (111... in bit representation). – fikr4n Jul 10 '14 at 02:32

3 Answers3

3

Note: this is pointless and only worth doing as an academic exercise.

The code makes the following assumptions (which are not guaranteed by the C standard):

  • int is 32-bit (1 sign bit followed by 31 value bits)
  • int is represented using 2's complement
  • Right-shifting a negative number does arithmetic shift, i.e. fill sign bit with 1

With these assumptions in place, x >> 31 will generate all-bits-0 for positive or zero numbers, and all-bits-1 for negative numbers.

So the effect of (~x & mask) + (x & ~mask) is the same as (x < 0) ? ~x : x .

Since we assumed 2's complement, ~x for negative numbers is -(x+1).

The effect of this is that if x is positive it remains unchanged. and if x is negative then it's mapped onto the range [0, INT_MAX] . In 2's complement there are exactly as many negative numbers as non-negative numbers, so this works.

Finally, we right-shift by n + ~0. In 2's complement, ~0 is -1, so this is n - 1. If we shift right by 4 bits for example, and we shifted all the bits off the end; it means that this number is representable with 1 sign bit and 4 value bits. So this shift tells us whether the number fits or not.

Putting all of that together, it is an arcane way of writing:

int x;

if ( x < 0 )
    x = -(x+1);

// now x is non-negative
x >>= n - 1;    // aka. x /= pow(2, n-1) 

if ( x == 0 )
    return it_fits;
else
    return it_doesnt_fit;
M.M
  • 138,810
  • 21
  • 208
  • 365
1

Here is a stab at it, unfortunately it is hard to summarize bitwise logic easily. The general idea is to try to right shift x and see if it becomes 0 as !0 returns 1. If right shifting a positive number n-1 times results in 0, then that means n bits are enough to represent it.

The reason for what I call a and b below is due to negative numbers being allowed one extra value of representation by convention. An integer can represent some number of values, that number of values is an even number, one of the numbers required to represent is 0, and so what is left is an odd number of values to be distributed among negative and positive numbers. Negative numbers get to have that one extra value (by convention) which is where the abs(x)-1 comes into play.

Let me know if you have questions:

int fitsBits(int x, int n) {
    int mask = x >> 31;

    /* -------------------------------------------------
    // A: Bitwise operator logic to get 0 or abs(x)-1
    ------------------------------------------------- */
    // mask == 0x0 when x is positive, therefore a == 0
    // mask == 0xffffffff when x is negative, therefore a == ~x
    int a = (~x & mask);
    printf("a = 0x%x\n", a);

    /* -----------------------------------------------
    // B: Bitwise operator logic to get abs(x) or 0
    ----------------------------------------------- */
    // ~mask == 0xffffffff when x is positive, therefore b == x
    // ~mask == 0x0 when x is negative, therefore b == 0
    int b = (x & ~mask);
    printf("b = 0x%x\n", b);

    /* ----------------------------------------
    // C: A + B is either abs(x) or abs(x)-1
    ---------------------------------------- */
    // c is either:
    // x if x is a positive number
    // ~x if x is a negative number, which is the same as abs(x)-1
    int c = (a + b);
    printf("c = %d\n", c);

    /* -------------------------------------------
    // D: A ridiculous way to subtract 1 from n
    ------------------------------------------- */
    // ~0 == 0xffffffff == -1
    // n + (-1) == n-1
    int d = (n + ~0);
    printf("d = %d\n", d);

    /* ----------------------------------------------------
    // E: Either abs(x) or abs(x)-1 is shifted n-1 times
    ---------------------------------------------------- */
    int e = (c >> d);
    printf("e = %d\n", e);

    // If e was right shifted into 0 then you know the number would have fit within n bits
    return !e;
}
asimes
  • 5,749
  • 5
  • 39
  • 76
0

You should be performing those operations with unsigned int instead of int.

Some operations like >> will perform an arithmetic shift instead of logical shift when dealing with signed numbers and you will have this sort of unexpected outcome.

enter image description here

A right arithmetic shift of a binary number by 1. The empty position in the most significant bit is filled with a copy of the original MSB instead of zero. -- from Wikipedia

With unsigned int though this is what happens:

enter image description here

In a logical shift, zeros are shifted in to replace the discarded bits. Therefore the logical and arithmetic left-shifts are exactly the same.

However, as the logical right-shift inserts value 0 bits into the most significant bit, instead of copying the sign bit, it is ideal for unsigned binary numbers, while the arithmetic right-shift is ideal for signed two's complement binary numbers. -- from Wikipedia

Havenard
  • 27,022
  • 5
  • 36
  • 62