隔离位并展平它们

时间:2017-08-21 19:00:32

标签: c++ bit-manipulation

问题

假设我有一个掩码mask和一个输入n,例如

mask = 0x10f3 (0001 0000 1111 0011)
n    = 0xda4d (1101 1010 0100 1101)

我希望 1)隔离屏蔽位(从n中删除不在mask中的位)

masked_n = 0x10f3 & 0xda4d = 0x1041 (0001 0000 0100 0001)

2)“展平”它们(摆脱mask中的零位并将这些相同的转移应用到masked_n)?

flattened_mask = 0x007f (0000 0000 0111 1111)

bits to discard         (___1 ____ 0100 __01)
first shift             (  __ _1__ __01 0001)
second shift            (       __ _101 0001)
result         = 0x0051 (0000 0000 0101 0001)

尝试过的解决方案

a)对于这种情况,可以制作一系列特定的位移:

result = (n & 0b10) | (n & 0b11110000) >> 2 | (n & 0b1000000000000) >> 6

b)更一般地说,也可以迭代mask的每个位并一次计算result一位。

for (auto i = 0, pos = 0; i < 16; i++) {
    if (mask & (1<<i)) {
        if (n & (1<<i)) {
            result |= (1<<pos);
        }
        pos++;
    }
}

问题

有没有一种更有效的方式来执行此操作,或者至少是临时的,但是无论位置如何都会有固定数量的操作?

1 个答案:

答案 0 :(得分:1)

一种更有效的通用方法是循环使用位,但只处理掩码中的位数,从循环中删除if (mask & (1<<i))测试,仅循环7次而不是16次为例面具。在循环find the rightmost bit of the mask的每次迭代中,用n测试它,在结果中设置相应的位,然后从掩码中删除它。

int mask = 0x10f3;
int n = 0xda4d;

int result = 0;
int m = mask, pos = 1;
while(m != 0)
{
    // find rightmost bit in m:
    int bit = m & -m;
    if (n & bit)
        result |= pos;
    pos <<= 1;
    m &= ~bit; // remove the rightmost bit from m
}
printf("%04x %04x %04x\n", mask, n, result);

输出:

10f3 da4d 0051

或者,可能不太可读,但没有bit临时变量:

if (n & -m & m)
    result |= pos;
pos <<= 1;
m &= m-1;

它是如何工作的?首先,考虑为什么m &= m-1清除最右边(最不重要)的设置位。你的(非零)掩码m将由一定数量的位组成,然后在最低有效位置中为1,然后为0或更多0:

e.g:

xxxxxxxxxxxx1000

减1给出:

xxxxxxxxxxxx0111

因此,所有高于最低有效设置位的位都将保持不变(因此将它们连接在一起使它们保持不变),最低有效位设置位从1变为0,而较低有效位则预先全部为0将它们与所有1进行对比使它们保持不变。净结果:清除最低有效设置位,其余字保持不变。

理解为什么m&amp; -m给出最低有效位集,将上述内容与2s补码中的知识相结合,-x = ~(x-1)