如何使用按位运算补充最右边的位,保持前导零位为零?

Bha*_*not 3 c bit-manipulation bit

如何补充具有前导零位的数值,以便前导零位保持为零,剩余的1位和0位将被补充?我想仅通过按位运算来执行此操作,而不必检查该值以确定该值中有多少前导零位.我可以使用哪些按位运算来仅隔离包含一个位或位打开的值的最低有效部分,并仅补充值的那部分,使前导零位保持不变.

例如,给出一个数字,比如9.

9将以无符号32位二进制形式表示为00 ... 01001.

为简单起见,请仅考虑8位形式.9 = 00001001

现在当我补充这个数字时,我会得到11110110.

但这不是我想要的.

我希望原始表示的前导0保持不变,并补充其余部分.

即对于9 = 00001001,前4个零应该保持为零,下一部分应该被称赞.所以我将有00000110即6.

我知道一个更长的方法:

  1. 查找给定数字的位数 b
  2. 找到给定数字的补充说 x
  3. 提取最后b

要么

  1. 减去(0xFF<<b)x

har*_*old 7

如果你有你想要影响的所有比特的掩码,那么它就是简单的x ^ mask(与1对XORing进行补充).

获得面具并不难:

mask = x;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
Run Code Online (Sandbox Code Playgroud)

这是32位.根据需要使用更多(或更少)步骤.

这个结构将最高设置位扩展到所有低位,方法是将该位已被复制到的所有位置并将其与该块右侧的位进行或运算,如下所示:

01000000
01100000
01111000
01111111
Run Code Online (Sandbox Code Playgroud)

也会复制最高设置位右侧的任何设置位,但它们不会干扰该过程,因为受其影响的任何位都位于最高设置位的右侧,因此无论如何都应该设置.

根据您所使用的机器,可能有更好的方法来获得该面具.以下是x64的一些选项.

使用shrx(Haswell +,轻松修改为更便携)

mov rdx, -1
bsr rax, rax
cmovz rdx, rax
xor eax, 63
shrx rax, rdx, rax
Run Code Online (Sandbox Code Playgroud)

使用shrxlzcnt(Haswell +)

lzcnt rax, rax
sbb rdx, rdx
not rdx
shrx rax, rdx, rax
Run Code Online (Sandbox Code Playgroud)

使用lzcntbzhi(Haswell +)

lzcnt rax, rax
mov edx, 64
sub edx, eax
mov rax, -1
bzhi rax, rax, rdx
Run Code Online (Sandbox Code Playgroud)

如果你可以反转位,像这样:

rbit r0, r0
neg r1, r0
or r0, r1
rbit r0, r0
Run Code Online (Sandbox Code Playgroud)

这取决于2的补码否定的性质,即最右边的设置位左边的所有位都被补充[1].与其补码的OR运算是1,因此-x | x将最右边的1传播到其左侧的所有位.这与我们需要的相反,但是通过快速位反转它很有用.

[1]:证明草图:-x = ~x + 1,考虑到最右边的位,包括最右边的1,在补码之后它们将是01*的形式,加上一个恢复原始的10*,而它上面的位保持补充.