Найдите старший значащий бит (крайним левым), который установлен в небольшом массиве

У меня есть немного реализации массива, где индексом 0th является MSB первого байта в массиве, 8-м индексом является MSB второго байта и т.д...

Что быстрый путь состоит в том, чтобы найти первым битом, который установлен в этом битовом массиве? Все связанные решения, которые я искал, находят первый младший значащий бит, но мне нужен первый старший значащий. Так, данный 0x00A1, я хочу 8 (так как это - 9-й бит слева).

38
задан Claudiu 6 April 2010 в 23:53
поделиться

7 ответов

GCC имеет __ builtin_clz , который преобразуется в BSR на x86 / x64, CLZ на ARM и т. Д. И эмулирует инструкцию, если оборудование не реализует ее.
Visual C ++ 2005 и более поздние версии имеют _BitScanReverse .

42
ответ дан 27 November 2019 в 03:08
поделиться

Хм, ваш тег указывает на 32-битный, но похоже, что вы используете 16-битные значения. Если вы имели в виду 32 бита, то я думаю, что ответ для 0x00a1 должен быть 24, а не 8.

Предполагая, что вы ищете индекс битов MSB с левой стороны и знаете, что будете иметь дело только с uint32_t, вот очевидный и простой алгоритм:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>

int main()
{
    uint32_t test_value = 0x00a1;
    int i;

    for (i=0; i<32; ++i)
    {
        if (test_value & (0x80000000 >> i))
        {
            printf("i = %d\n", i);
            exit(0);
        }
    }

    return 0;
}
0
ответ дан 27 November 2019 в 03:08
поделиться

Вот простой алгоритм перебора для массива байтов произвольного размера:

int msb( unsigned char x);  // prototype for function that returns 
                            //  most significant bit set

unsigned char* p;

for (p = arr + num_elements; p != arr;) {
    --p;
    if (*p != 0) break;
}

// p is with pointing to the last byte that has a bit set, or
//  it's pointing to the first byte in the array

if (*p) {
    return ((p - arr) * 8) + msb( *p);
}

// what do you want to return if no bits are set?
return -1;

Я оставлю читателю возможность придумать подходящую функцию msb(), а также оптимизацию для работы с массивами данных размером int или long long.

0
ответ дан 27 November 2019 в 03:08
поделиться

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

Ознакомьтесь с некоторыми реализациями здесь (в разделе «Целочисленный журнал с основанием 2»). Если вы используете GCC, ознакомьтесь с функциями __ builtin_clz и __ builtin_clzl (которые делают это для ненулевых целых чисел без знака и длинных чисел без знака, соответственно). «Clz» означает «подсчитывать ведущие нули», что является еще одним способом описания той же проблемы.

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

13
ответ дан 27 November 2019 в 03:08
поделиться

Найдите в инструкции x86 asm BSR (битовое сканирование в обратном направлении) как можно быстрее это сделать. Из документа Intel: Ищет в исходном операнде (второй операнд) старший значащий бит набора (1 бит). Если найден старший значащий 1 бит, его битовый индекс сохраняется в операнде назначения (первый операнд).

5
ответ дан 27 November 2019 в 03:08
поделиться

Два лучших известных мне способа сделать это на чистом C:

Сначала линейный поиск в массиве байтов/слов, чтобы найти первый ненулевой байт/слово, затем развёрнутый бинарный поиск найденного байта/слова.

if (b>=0x10)
  if (b>=0x40)
    if (b>=0x80) return 0;
    else return 1;
  else
    if (b>=0x20) return 2;
    else return 3;
else
  if (b>=0x4)
    if (b>=0x8) return 4;
    else return 5;
  else
    if (b>=0x2) return 6;
    else return 7;

3 (BTW это log2(8)) условных перехода для получения ответа. На современных машинах x86 последний будет оптимизирован до условного mov.

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

Смежная тема, которую вы, возможно, захотите изучить, - целочисленные функции log2. Насколько я помню, в ffmpeg есть хорошая реализация.

Edit: Вы можете превратить приведенный выше двоичный поиск в двоичный поиск без ветвления, но я не уверен, будет ли это более эффективно в данном случае...

1
ответ дан 27 November 2019 в 03:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: