Количество бит, установленных в числе

Следующая магическая формула, которая дает количество бит, установленных в числе.

/*Code to Calculate count of set bits in a number*/
int c;
int v = 7;
v = v - ((v >> 1) & 0x55555555);                    // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);     // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
printf(" Number of Bits is %d",c);
/*-----------------------------------*/

от: http://graphics.stanford.edu/~seander/bithacks.html

Может ли кто-нибудь объяснить мне обоснование этого?

Ответ 1

Это действительно очень умный код и, очевидно, гораздо труднее понять, чем простой наивный цикл.

Для первой строки просто возьмите четырехбитное количество и назовите его abcd. Код в основном делает это:

abcd - ((abcd >> 1) & 0101) = abcd - (0abc & 0101) = abcd - 0a0c

Итак, в каждой группе из двух битов он вычитает значение высокого бита. Что это значит?

11 - 1 -> 10 (two bits set)
10 - 1 -> 01 (one bit set)
01 - 0 -> 01 (one bit set)
00 - 0 -> 00 (zero bits set)

Итак, эта первая строка устанавливает каждую последовательную группу из двух битов в число бит, содержащихся в исходном значении, - она ​​подсчитывает биты, установленные в группах по два. Вызовите полученную четырехбитную величину abcd.

Следующая строка:

(ABCD & 0011) + ((ABCD>>2) & 0011) = 00CD + (AB & 0011) = 00CD + 00AB

Итак, он берет группы из двух бит и объединяет пары. Теперь каждая четырехбитовая группа содержит количество бит, установленных в соответствующих четырех битах ввода.

В следующей строке v + (v >> 4) & 0xF0F0F0F (которая анализируется как (v + (v >> 4)) & 0xf0f0f0f) делает то же самое, добавляя пары четырехбитовых групп вместе, чтобы каждая восьмибитовая группа (байт) содержала бит-счетчик соответствующий входной байт. Теперь мы имеем число, подобное 0x0e0f0g0h.

Обратите внимание, что умножение байта в любой позиции на 0x01010101 будет копировать этот байт до самого значимого байта (а также оставить некоторые копии в нижних байтах). Например, 0x00000g00 * 0x01010101 = 0x0g0g0g00. Итак, умножив 0x0e0f0g0h, мы оставим e+f+g+h в верхнем байте; >>24 в конце извлекает этот байт и оставляет вас с ответом.