Counting Bits - Better Loop
Counting the bits set: the better loop approach.
Better looping
This algorithm, described by Kernighan, addresses problem 3 (the number of times we loop) and only loops as many times as there are bits set, regardless of their position.
1
2
3
4
5
6
7
8
int count_bits_2(unsigned int x) {
int count{ 0 };
while (x != 0) {
++count;
x &= (x - 1);
}
return count;
}
If the loop above looks difficult to understand, but very clever, it’s because
it is. It relies on the fact that x - 1 flips all the bits up to and
including the last bit that is set.
Detailed explanation
If x is not zero and we peform a iteration in the loop, the representation of
x as bits can be divided into three components (from LSB/right to MSB/left):
- a suffix range of all zeros following (but not including) the last bit set up to the LSB (the least significant bit)
- the last bit that is set: the bit counted in this iteration
- a prefix range of mixture of ones and zeros from the MSB (most significant bit) to (but not including) the last bit that is set: this is the prefix range of bits
Either of the two ranges around the bit counted in this iteration can be empty.
x at the start:
MSB LSB
+----------------------------------+---+---------------------+
| 0 | 1 | 0 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 0 | ... | 0 |
+----------------------------------+---+---------------------+
| | | |
|<- prefix (mixture of 0s and 1s)->| ^ |<- all zero range ->|
|
the last bit set: the bit counted in this iteration
When we substract 1, all bits up to and including the last bit set flip:
- the suffix range of all zeros becomes a range of all ones
- the bit counted in this iteration becomes 0
- the prefix range stays the same
x - 1:
MSB LSB
+----------------------------------+---+---------------------+
| 0 | 1 | 0 | ... | 0 | 1 | 1 | 0 | 0 | 1 | 1 | ... | 1 |
+----------------------------------+---+---------------------+
| | | |
|<- stays the same ->| ^ |<- all ones range ->|
|
the bit counted in this iteration flips to 0
After bitwise AND between the two values above we get the value for the next iteration.
x at the end (after "x &= (x-1)")
MSB LSB
+----------------------------------+---+---------------------+
| 0 | 1 | 0 | ... | 0 | 1 | 1 | 0 | 0 | 0 | 0 | ... | 0 |
+----------------------------------+---+---------------------+
| | | |
|<- new prefix range ->| ^ |<- new all zero range ->|
|
the bit to be counted in the next iteration
This value preseves the invariant of the loop (three components) or x
becomes zero.