Как подсчитать количество нулевых битов в целом?

Как я могу найти количество "нулевых" битов в C++. Предположим, у меня есть целое число;

int value = 276; 

для которого у меня есть биты 100010100, но как подсчитать нули?

11 ответов


самый простой самый наивный способ-просто перебирать биты и считать:

size_t num_zeroes = 0;

for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i)
{
  if ((value & (1 << i)) == 0)
    ++num_zeroes;
}

есть все количество лучших (для разных значений "лучше") способов, но это довольно ясно, очень лаконично (с точки зрения кода) и не требует кучу настроек.

одна микро-оптимизация, которую можно считать улучшением, заключается в том, чтобы не вычислять маску для тестирования каждого бита, а сдвигать значение и всегда проверять самый правый бит:

for(size_t i = 0; i < CHAR_BIT * sizeof value; ++i, value >>= 1)
{
  if ((value & 1) == 0)
    ++num_zeroes;
}

если вы хотите эффективности, то есть хорошая реализация в книге "хакеры восторг"

22 инструкции бесплатно.

unsigned int count_1bits(unsigned int x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

unsigned int count_0bits(unsigned int x)
{
    return 32 - count_1bits(x);
}

я попытаюсь объяснить, как это работает. Это алгоритм "разделяй и властвуй".

(x >> 1) & 0x55555555

сдвигает все биты на 1 шаг вправо и принимает наименее значительный бит каждой пары битов.

0x55555555 -> 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 (16x2 bit pairs)

таким образом, в основном у вас будет следующая таблица всех 2-битных перестановок.

1. (00 >> 1) & 01 = 00
2. (01 >> 1) & 01 = 00
3. (10 >> 1) & 01 = 01
4. (11 >> 1) & 01 = 01

x - ((x >> 1) & 0x55555555);

затем вы вычитаете их из несмещенных пар.

1. 00 - 00 = 00 => 0 x 1 bits
2. 01 - 00 = 01 => 1 x 1 bits
3. 10 - 01 = 01 => 1 x 1 bits
4. 11 - 01 = 10 => 2 x 1 bits

x = x - ((x >> 1) & 0x55555555);

Итак, теперь мы изменили каждую 2-битную пару так, чтобы их значение теперь было количеством битов их соответствующих исходных 2-битных пар... и тогда мы продолжаем аналогичным образом с 4-битных групп, 8 бит групп, 16 бит групп и окончательной 32 бит.

если вы хотите лучшее объяснение купите книгу, там много хороших объяснений и обсуждений альтернативных алгоритмов и т. д...


вы можете сделать 32 минус количество бит set.


Если вы используете GCC, вы можете попробовать встроенные функции:

int __builtin_popcount (unsigned int x) 
int __builtin_ctz (unsigned int x)
int __builtin_clz (unsigned int x)

посмотреть документация GCC для сведения.


Керниган способом подсчета бит

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

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

Я также рекомендую ссылку выше для различных других способов решения этой и других типов задач, связанных с битом. Существует также однострочный пример получения количества битов, реализованного в макросах.


на сегодняшний день наиболее очевидным решением является таблица поиска.

/* Assuming CHAR_BITS == 8 */
int bitsPerByte[256] = { 8, 7, 7, 6, /* ... */ };
int bitsInByte(unsigned char c) { return bits[c]; }

есть отличная книга для такого рода вещей: восторг хакера (да, название отстой : это не имеет ничего общего с безопасностью, но исключительно бит-twiddling). Он предоставляет несколько алгоритмов для подсчета бит "1", лучшее также можно найти здесь (хотя в книге есть объяснения, что этот сайт не так).

Как только вы узнаете количество бит "1", просто вычитайте его до количества бит в вашем представлении типа.


Я удивлен, что никто не упомянул этот:

int num_zero_bits = __builtin_popcount(~num);

это даст количество нулевых битов в num при использовании с GCC.


сделайте комплимент, затем сосчитайте 1s.

count_zero_bits (x ) = count_one_bits (~x );

реализуйте код для подсчета единиц.

template< typename I > 
int count_one_bits( I i )
{
   size_t numbits = 0;
   for( ; i != 0; i >>= 1 )
   {
      numbits += i&1;
   }
}

хотя есть проблема с моей функцией, если я отрицательное число, потому что >> поместит 1 бит в правую сторону, так что вы получите бесконечный цикл. Если существует шаблонный способ применения неподписанного типа, который был бы идеальным.

после этого затем:

template< typename I > int count_zero_bits( I i )
{
   return count_one_bits( ~i );
}

будет работать.


по моему мнению, самый простой способ получить количество нулевых битов в натуральном числе-это следующий фрагмент кода.

int get_zero_bit_count(int num)
{
    int cnt = 0;

    while(num > 0)
        {
            int and_num = num & 1;

            if (and_num != num) cnt++;

            num >>= 1; 
        }

        return cnt;
    }

этот фрагмент кода легко понять и является selp explainatory . Это хорошо работает для целых положительных чисел.


расширение ответа ронага, о котором упоминали другие пользователи, приводит к неправильным результатам (его алгоритм работает только до значения x = 15), вот обновленная версия алгоритма:

uint8_t count_1bits(uint32_t x) {
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
    x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
    x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);
    return x & 0x3F;
}

uint8_t count_0bits(uint32_t x)    {
    return 32 - count_1bits(x);
}

объяснение первой строки из ronag является правильным, однако остальные строки используют другой подход. В первой строке, через смещение и вычитание, каждая 2-битная пара будет содержать количество битов, которые были установлены в этой паре в исходном числе. Этот остальные строки рекурсивно складывают эти числа вместе, добавляя lsb каждой 2n-разрядной группы к msb этой пары, сдвинутой на n, так что 2n-разрядная группа содержит количество битов, которое было установлено в этой группе в исходном номере:

01110110: 0111 (7 bits were set in the original number) 0110 (6 bits were set in the original number)
-> 01110110 & 00001111 + (01110110 >> 4) & 00001111
= 0110 + 0111
= 1101

приведенный выше алгоритм работает для 32-битных целых чисел, но может быть легко адаптирован путем изменения констант на правильную битовую длину, чтобы шаблон оставался неизменным (например, 0x5555... = 0101..., 0x0f0f... = 00001111... так далее.) и добавление / удаление соответствующих сдвигов