Can someone explain how this bitCount code works?

7.3k views Asked by At

This is code that my TA help me get, but then I completely forgot how it worked exactly since I can't seem to get the right answer, and the interview grading is tomorrow. If anybody can help please do. Thanks

* bitCount - returns count of number of 1's in word
*   Examples: bitCount(5) = 2, bitCount(7) = 3
*   Legal ops: ! ~ & ^ | + << >>
*   Max ops: 40
*   Rating: 4
*/
int bitCount(int x) {
    int m4 = 0x1 | (0x1<<8) | (0x1<<16) | (0x1<<24);
    int m1 = 0xFF; 
    int s4 = (x&m4) + ((x>>1)&m4) + ((x>>2)&m4) + ((x>>3)&m4) + ((x>>4)&m4) + ((x>>5)&m4) + ((x>>6)&m4) + ((x>>7)&m4);
    int s1 = (s4&m1) + ((s4>>8)&m1) + ((s4>>16)&m1) + ((s4>>24)&m1);
    return s1;
}
2

There are 2 answers

0
Ian On BEST ANSWER

It is often easier to see what happen in the bit-related operation when you convert it to its bit representation:

Assuming the int is 32-bit, then the following are the m4 & m1 in their bit-representation:

0000 0001 0000 0001 0000 0001 0000 0001 //m4, masking across the 4 bytes
0000 0000 0000 0000 0000 0000 1111 1111 //m1, masking only 1 byte, the Least Significant Byte (LSB)

I suppose m stands for mask, while 4 & 1 refer to 4 bytes and 1 byte respectively

And then the tricky part is the s4. You might need to examine it step by step to get the idea of what it is.

Firstly, note the right-bitwise-shift and the masking with m4:

pqrs tuvw pqrs tuvw pqrs tuvw pqrs tuvw // x
0000 0001 0000 0001 0000 0001 0000 0001 //m4
---------------------------------------- &
0000 000w 0000 000w 0000 000w 0000 000w //x&m4

pqrs tuvw pqrs tuvw pqrs tuvw pqrs tuvw // x
---------------------------------------- >> 1
0pqr stuv wpqr stuv wpqr stuv wpqr stuv // x >> 1
0000 0001 0000 0001 0000 0001 0000 0001 //m4
---------------------------------------- &
0000 000v 0000 000v 0000 000v 0000 000v //(x>>1)&m4
.
.
pqrs tuvw pqrs tuvw pqrs tuvw pqrs tuvw // x
---------------------------------------- >> 7
0000 000p qrst uvwp qrst uvwp qrst uvwp // x >> 7
0000 0001 0000 0001 0000 0001 0000 0001 //m4
---------------------------------------- &
0000 000p 0000 000p 0000 000p 0000 000p //(x>>7)&m4

And secondly, note the addition of the 8 elements obtained from above results:

0000 000w 0000 000w 0000 000w 0000 000w //x&m4
0000 000v 0000 000v 0000 000v 0000 000v //(x>>1)&m4
.
.
0000 000p 0000 000p 0000 000p 0000 000p //(x>>7)&m4
---------------------------------------- +
//Resulting in s4

Thus, since p to w each can only be 0 or 1 and you have eight of such element, consequently, per 8-bit segment you have:

p+q+r+s+t+u+v+w // each element is either 0 or 1

From there, it can be seen clearly that the result for the addition 8 elements above would range from 0 to 8.

That is, you will get 0000 0000 (0 in decimal representation - if all is 0) to 0000 1000 (8 in decimal representation - if all is 1) per 8-bit segment.

Consequently, you will have the s4 in the following format:

0000 abcd 0000 efgh 0000 ijkl 0000 mnop // s4

Where abcd, efgh, ijkl, and mnop are the result of additions p to w per byte.

Now, notice that you get the sum of the number of bits in x spread across the 4 bytes.

(sum I suppose this is what s in the variables stands for - sum)

0000 abcd //byte 1, left most, MSB
0000 efgh //byte 2, second from left
0000 ijkl //byte 3, second from right
0000 mnop //byte 4, rightmost, LSB

Thus, you need to combine the result in those four bytes in s4 to find the sum in one byte

(and I suppose, this is what s1 stands for: sum in one byte)

You get s1 by:

  1. Shifting s4 with 0, 8, 16, 24
  2. Mask each of them with 0xFF
  3. And combine (add) the results.

Hence, the following operations (in the bit level) occur:

0000 abcd 0000 efgh 0000 ijkl 0000 mnop // s4
0000 0000 0000 0000 0000 0000 1111 1111 //m1 
--------------------------------------- &
0000 0000 0000 0000 0000 0000 0000 mnop

0000 0000 0000 abcd 0000 efgh 0000 ijkl // s4 >> 8
0000 0000 0000 0000 0000 0000 1111 1111 //m1 
--------------------------------------- &
0000 0000 0000 0000 0000 0000 0000 ijkl

.
.

0000 0000 0000 0000 0000 0000 0000 abcd // s4 >> 24
0000 0000 0000 0000 0000 0000 1111 1111 //m1 
--------------------------------------- &
0000 0000 0000 0000 0000 0000 0000 abcd

It can be seen that you simply have the following additions of four elements:

0000 0000 0000 0000 0000 0000 0000 mnop //0 to 8
0000 0000 0000 0000 0000 0000 0000 ijkl //0 to 8
0000 0000 0000 0000 0000 0000 0000 efgh //0 to 8
0000 0000 0000 0000 0000 0000 0000 abcd //0 to 8
--------------------------------------- +
//Final result, s1

You have simple addition of four numbers, each having value of 0 to 8. Thus, it must result in a value of 0 to 32 (0 is when all is 0, 32 is when all is 8), which is the number of bit 1 in the variable x. And thus the function is named bitCount.

That is how the function works.


Final note: knowing this, you could even change m1 with 0x0F (instead of 0xFF) and the result would still be the same.

0
Ash On

After this statement :

    int s4 = (x&m4) + ((x>>1)&m4) + ((x>>2)&m4) + ((x>>3)&m4) + ((x>>4)&m4) + ((x>>5)&m4) + ((x>>6)&m4) + ((x>>7)&m4);

As there are 4 bytes in S4, then 1 byte of it will have the number of 1's in corresponding byte of x So, clearly each byte of s4 hold the number of 1's in the corresponding byte of x.

Then in statement : int s1 = (s4&m1) + ((s4>>8)&m1) + ((s4>>16)&m1) + ((s4>>24)&m1);

Now 1 byte of s4 will have the number of 1's in corresponding byte of x, then right shift of s4 to 8 bits will give the number of 2's in byte 2 of x, so on for 4 bytes. Then adding all will give the number of 1's in x.