增加“掩码”位集


80

我目前正在编写树枚举器的过程中遇到以下问题:

我正在查看被屏蔽的位集,即设置的位是掩码的子集的位集,即0000101具有mask的位集1010101。我要完成的是增加位集,但仅是相对于掩码位。在此示例中,结果将为0010000。为了使其更清晰,仅提取被屏蔽的位,即0011,将它们增加到0100并再次将它们分配给屏蔽位,得到0010000

除了使用bitcans和前缀掩码的组合手动执行操作之外,有人能看到实现此目的的有效方法吗?

Answers:


123

只需在非掩码位中填充1,以便它们传播进位:

// increments x on bits belonging to mask
x = ((x | ~mask) + 1) & mask;

11
真是个好主意……我说的几乎没有魔术:)
Eugene Sh。

8
@EugeneSh。永远不要相信事实并非如此。
zch

23
自从OP被接受以来,对OP可能并不重要,但应该注意的是,这将使非掩码位清零。如果其他地方需要它们,必须更小心地进行更换x。可能的x = (x & ~mask) | (((x | ~mask) + 1) & mask);
TripeHound17年

2
@TripeHound如果不需要它们,即使使用位掩码也有什么意义呢?
someonewithpc

1
@someonewithpc不确定您要说什么/要问。我不知道为什么OP需要增加一组不相邻的位,所以我不知道原始值中的其他位是否重要。例如,如果原始值是0101101(例如.1.1.0.,在非掩码位和0.0.1.1“计数器”中),则它们将是需要的 01110000.1.0.0保留时的新“计数器” .1.1.0.)还是0010000可接受的。这个答案(可能还有其他答案,尽管我没有检查过)给了后者。如果需要,我的版本应该提供前者。
TripeHound17年

20

与接受的答案相比,虽然不直观,但仅需3个步骤:

x = -(x ^ mask) & mask;

可以按照zch的建议进行验证:

  -(x ^ mask)
= ~(x ^ mask) + 1  // assuming 2's complement
= (x ^ ~mask) + 1
= (x | ~mask) + 1  // since x and ~mask have disjoint set bits

然后,它等同于接受的答案。


2
zch的回答非常直观,由于他的明确解释,我可以立即看到它是正确的。这个答案的逻辑是什么?该公式如何产生所需的效果?我对发现的过程,洞察的本质感到好奇。
FooF

我认为,-(x ^ mask) == (x | ~mask) + 1只要您证明只要x是mask的子集,然后引用我的答案,您的验证就会简单得多。
zch

5
-(x^mask) == ~((x ^ mask) - 1) == ~(x ^ mask) + 1 == (x ^ ~mask) + 1 == (x | ~mask) + 1。最后一个方程式成立是因为位集是不相交的,其他位集始终为真(至少在2补码中)。
zch

1
那些对我得出此答案的步骤感到好奇的人可以参考此页面
nglee

5
可能值得指出的是,它们并没有优化相同的东西,这通常与那些花哨的人有关:godbolt.org/g/7VWXas-尽管实际上哪个更短似乎取决于编译器。不知道哪个会更快,或者差异是否很大。
Leushenko

7

如果迭代的顺序不是那么重要,并且递减操作可以满足您的需求,则可以仅使用两个操作:

让我们开始

x = mask

并获得先前的价值

x = (x - 1) & mask

x - 1部分将最后一个非零位更改为零,并将所有较低有效位设置为1。然后& mask部分仅保留其中的掩码位。


2个操作,很好。但是我认为这是相同的方法,只是通过零传播借位,而不是通过1传播。
zch

@zch,是的,谢谢。我改写了答案
戴尔

仅当x以所有非掩码位清零开头时才有效。
Jasen

@Jasen,当然。但是设置那些非掩码位并不难。其他答案也有类似的问题。
DAle
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.