【发布时间】:2016-05-19 04:31:04
【问题描述】:
给定:
- 一个位掩码
a(比如std::uint64_t),其中至少包含一组(1)位。 - 选择器位掩码
b,它是a(即a & b == b)的子集,并且至少设置了一个位。
我想在a 中选择与b 中的位重叠的连续 1 位跨度:
a = 0b1111001110001100;
b = 0b0000001010001000;
//c=0b0000001110001100
// XXXX YYY ZZ
c 中的 XXXX 组为 0,因为 b & XXXX 为 false。复制 ZZ 组是因为 b 设置了 Z 位之一。出于同样的原因,YYY 组也设置在c 中。 注意b 可以在a 的一个组中拥有多个设置位。
因此,对于a 中每个连续的1s 组,如果b 在任何这些位置有1,则设置c 中的所有这些位。一个更复杂的例子:
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)或位操作技巧可以让我有效地从a 和b 计算c? (即没有循环)?
补充:
使用丹尼斯回答中的提示,我只能想象基于循环的算法:
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;
【问题讨论】:
-
“一个段”是否包括长度为 1 的段?
-
@M.M 是的,1-segment 只是 1-s 的非零长度序列。
-
我花了大约 10 分钟来解码您的描述,因此我进行了编辑以澄清未来的读者。我假设您无意中遗漏了英特尔的 BMI1/BMI2 扩展,而不是因为Haswell is too new。您确实提到了 AVX,这绝不是基线:即使在 Skylake Celeron/Pentium 上也不支持,只有 i3 和更高版本。谢谢,英特尔 :(
-
实际上,没有任何 BMI 指令本身显然是有用的。不过,可能
pext/pdep作为构建块。你绝对需要你指定格式的输出吗? Denis 的答案丢失了有关每个组有多大的信息,但每个匹配组的结果中确实有 1。 -
@PeterCordes 感谢您的指正。非常感谢。
标签: c++ x86 bit-manipulation bmi