Каков самый быстрый способ вычисления количества битов, необходимых для хранения числа

Я пытаюсь оптимизировать некоторые процедуры упаковки и распаковки бит. Чтобы сделать упаковку, мне нужно вычислить количество битов, необходимых для хранения целочисленных значений. Вот текущий код.

if (n == -1) return 32;
if (n == 0) return 1;
int r = 0;
while (n)
{
    ++r;
    n >>= 1;
}
return r;

6 ответов


вы хотите определить целочисленную базу журнала 2 числа (L=самый высокий набор битов). Страница "Bit Twiddling Hacks" Шона Андерсона имеет несколько методов, начиная от очевидного подсчета битов в цикле до версий, которые используют поиск таблицы. Обратите внимание, что большинство продемонстрированных методов необходимо будет немного изменить для работы с 64-битными ints, если такая переносимость важна для вы.

просто убедитесь, что любое смещение, которое вы используете для разработки самого высокого набора битов, должно быть сделано " на unsigned версия числа, так как реализация компилятора может или не может подписать расширение >> операция со знакомым значением.


непереносимо используйте код операции bit-scan-reverse, доступный в большинстве современных архитектур. Это выставлено как внутренние в Visual C++.

переносимо, код в вопросе не нуждается в обработке edge-case. Почему вам нужен один бит для хранения 0? В любом случае, я не буду обращать внимания на суть проблемы. Кишки можно сделать эффективно таким образом:

if (n >> 16) { r += 16; n >>= 16; }
if (n >>  8) { r +=  8; n >>=  8; }
if (n >>  4) { r +=  4; n >>=  4; }
if (n >>  2) { r +=  2; n >>=  2; }
if (n - 1) ++r;

то, что вы пытаетесь сделать, это найти наиболее значимый бит. Некоторые архитектуры имеют специальную инструкцию именно для этой цели. Для тех, кто этого не делает, используйте метод поиска таблицы.

создайте таблицу из 256 записей, в которой каждый элемент определяет самый верхний бит.

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

Я позволю тебе взять остальное отсюда.


выполнить бинарный поиск вместо линейного поиска.

if ((n >> 16) != 0)
{
    r += 16;
    n >>= 16;
}

if ((n >> 8) != 0)
{
    r += 8;
    n >>= 8;        
}

if ((n >> 4) != 0)
{
    r += 4;
    n >>= 4;        
}

// etc.

Если ваше оборудование имеет bit-scan-reverse, еще более быстрым подходом было бы написать вашу процедуру на языке ассемблера. Чтобы сохранить ваш код портативным, вы можете сделать

#ifdef ARCHITECTURE_WITH_BSR
   asm // ...
#else
   // Use the approach shown above
#endif

вам нужно будет проверить время выполнения, чтобы вычислить степень детализации, но я предполагаю, что выполнение 4 бит за раз, а затем возврат к одному биту за раз сделает его быстрее. Операции журнала, вероятно, будут медленнее, чем логические/битные операции.

if (n < 0) return 32;
int r = 0;
while (n && 0x7FFFFFF0) {
  r+=4;
  n >>= 4; }
while (n) {
  r++;
  n >>= 1; }
return r;

number_of_bits = log2(integer_number)

округлено до высшего целого числа.