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.