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

Дано:

  • Битовая маска a (скажем, std::uint64_t), которая содержит хотя бы один установленный (1) бит.
  • Битовая маска селектора b, которая является подмножеством a (т. е. a & b == b) и имеет хотя бы один установленный бит.

Я хочу выбрать диапазоны смежных 1-битов в a, которые перекрываются с битом в b:

a = 0b1111001110001100;
b = 0b0000001010001000;
//c=0b0000001110001100
//    XXXX  YYY   ZZ

Группа XXXX равна 0 в c, потому что b & XXXX ложно. Группа ZZ копируется, поскольку в b установлен один из битов Z. Группа YYY также установлена ​​в c по той же причине. Обратите внимание, что b может иметь несколько установленных битов в одной группе в a.

Таким образом, для каждой непрерывной группы 1s в a установите все эти биты в c, если b имеет 1 в любой из этих позиций. Более сложный пример:

std::uint64_t a = 0b1101110110101;
std::uint64_t b = 0b0001010010001;
// desired   c == 0b0001110110001
// contiguous groups   ^^^ ^^   ^  that overlap with a 1 in b

assert(a & b == b);           // b is a subset of a

std::uint64_t c = some_magic_operation(a, b);
assert(c == 0b0001110110001);

Существуют ли какие-либо битовые логические инструкции/внутренние элементы (MMX, SSE, AVX, BMI1/BMI2) или трюки с битовыми манипуляциями, которые позволяют мне эффективно вычислять c из a и b? (т.е. без петель)?


ДОПОЛНИТЕЛЬНЫЙ:

Используя подсказку из ответа Дениса, я могу только представить алгоритм на основе цикла:

std::uint64_t a = 0b0110111001001101;
std::uint64_t b = 0b0100101000001101;
assert(a & b == b); // subset

std::cout << std::bitset< 16 >(a) << std::endl;
std::cout << std::bitset< 16 >(b) << std::endl;
std::uint64_t x = (a + b) & ~a;
std::uint64_t c = 0;
while ((x = (a & (x >> 1)))) { // length of longest 1-series times
    c |= x;
}
std::cout << std::bitset< 16 >(c) << std::endl;

person Tomilov Anatoliy    schedule 19.05.2016    source источник
comment
включает ли отрезок отрезки длины 1?   -  person M.M    schedule 19.05.2016
comment
@MM Да, 1-сегмент - это просто последовательность единиц ненулевой длины.   -  person Tomilov Anatoliy    schedule 19.05.2016
comment
Мне потребовалось около 10 минут, чтобы расшифровать ваше описание, поэтому я внес правку, чтобы прояснить ситуацию для будущих читателей. Я предполагаю, что вы упустили расширения Intel BMI1/BMI2 случайно, а не потому, что Haswell слишком новый. Вы упомянули AVX, который ни в коем случае не является базовым: он не поддерживается даже на Skylake Celeron/Pentium. , только i3 и выше. Спасибо, Интел :(   -  person Peter Cordes    schedule 19.05.2016
comment
На самом деле, ни одна из инструкций ИМТ сама по себе явно не полезна. Однако, возможно, pext / pdep в качестве строительных блоков. Вам абсолютно необходим вывод в указанном вами формате? Ответ Дениса теряет информацию о том, насколько большой была каждая группа, но имеет 1 в результате для каждой подходящей группы.   -  person Peter Cordes    schedule 19.05.2016
comment
@PeterCordes Спасибо за исправление. Я очень благодарен.   -  person Tomilov Anatoliy    schedule 19.05.2016
comment
@PeterCordes Мне нужны (выбранные) части маски. Какой формат может быть полезен тому, кого вы можете предложить?   -  person Tomilov Anatoliy    schedule 19.05.2016
comment
Что делать с получившейся маской? Есть ли способ изменить код/алгоритм, который использует результат, чтобы упростить расчет? Я не имею в виду ничего конкретного, кроме выходного формата Дениса ((a+b)&~a). Если бы b имел только 1 в младшем бите группы, мы могли бы, возможно, сделать ( (a+b)&~a ) - b, чтобы заимствование превратило группы обратно в все 1, остановившись на 1 бите из выражения Дениса. Есть ли шанс, что у b будет такое дополнительное условие?   -  person Peter Cordes    schedule 19.05.2016
comment
@PeterCordes В моем реальном случае - нет. Многие биты селектора могут совпадать с определенной 1-й серией исходной битовой маски.   -  person Tomilov Anatoliy    schedule 19.05.2016
comment
@Orient: вопрос здесь, вероятно, дублирует ваш вопрос. Тем не менее, вас может заинтересовать мое решение, оптимизированное под SIMD.   -  person wim    schedule 06.06.2019


Ответы (1)


В случае uint64_t вы можете проделать этот трюк:

Давайте установим a = 0b11011101101. Важно иметь хотя бы один нулевой бит. Битовая маска имеет 4 отдельные области, заполненные 1 битами. Если вы сделаете c=a+(a&b), то каждая заполненная 1 область переполнится, если хотя бы один бит b в этой области установлен. Таким образом, вы можете проверить, какая область была переполнена. Например, если вам нужны 1-биты во 2-й и 3-й областях a, вы можете сделать так:

    assert(c & 0b00100010000);
    //              ^^^ ^^ this segments overflows
person Denis Sheremet    schedule 19.05.2016
comment
В моем реальном случае MSB равен нулю. Хороший. - person Tomilov Anatoliy; 19.05.2016
comment
объяснение: a&b == b, так что этот шаг лишний. Маскирование с помощью & ~a удаляет все биты, кроме переносов выбранных групп в a. - person Peter Cordes; 19.05.2016