C++ 递增';蒙面';位集

C++ 递增';蒙面';位集,c++,c,bit-manipulation,intrinsics,C++,C,Bit Manipulation,Intrinsics,我目前正在编写一个树枚举器,遇到以下问题: 我正在研究掩码位集,即位集是掩码子集的位集,即带有掩码的0000101。我想要完成的是增加位集,但只针对屏蔽位。在本例中,结果将是0010000。为了更清楚一点,只提取屏蔽位,即0011,将它们增加到0100,然后再次将它们分配到屏蔽位,给出0010000 除了使用位扫描和前缀掩码的组合手动执行操作外,有人认为有一种有效的方法可以做到这一点吗?只需将非掩码位填充为1,以便它们传播进位: // increments x on bits belonging

我目前正在编写一个树枚举器,遇到以下问题:

我正在研究掩码位集,即位集是掩码子集的位集,即带有掩码的
0000101
。我想要完成的是增加位集,但只针对屏蔽位。在本例中,结果将是
0010000
。为了更清楚一点,只提取屏蔽位,即
0011
,将它们增加到
0100
,然后再次将它们分配到屏蔽位,给出
0010000


除了使用位扫描和前缀掩码的组合手动执行操作外,有人认为有一种有效的方法可以做到这一点吗?

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

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

虽然与公认的答案相比并不直观,但这仅在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

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

如果迭代的顺序没有那么重要,并且减量运算可以满足您的需要,那么可以只使用两种运算:

让我们从

x=mask

并使用

x=(x-1)和掩码


x-1
部分将最后一个非零位更改为零,并将所有低有效位设置为1。然后,
&mask
部分只留下掩码位。

这是一个很好的技巧……几乎就像我说的魔法一样,没有:)@EugeneSh。千万不要相信不是这样。可能对OP不重要,因为他们接受了,但也许应该注意,这将使非掩码位归零。如果其他地方需要它们,您必须更加小心地更换
x
。可能
x=(x&~mask)|((x&~mask)+1)和mask)
@TripeHound如果不需要它们,甚至使用位掩码还有什么意义?@someonewithpc不确定你想说什么/问什么。我不知道为什么OP需要增加一组非相邻的位,所以我不知道原始值中的其他位是否重要。例如,如果原始值为
0101101
(例如,在非掩码位中为
1.1.0.
,在“计数器”中为
0.0.1.1
),他们是否需要
0111000
(在保留
.1.1.0.
的情况下为
0.1.0.
的新“计数器”)或仅
0010000
可接受。这个答案(可能还有其他答案,尽管我没有检查)给出了后者;如果需要的话,我的版本应该给出前者。zch的答案非常直观,我可以立即看到它是正确的,因为他的解释很清楚。这个答案的逻辑是什么?这个公式是如何产生预期效果的?我对发现的过程很好奇,这里的洞察的本质也很好奇。我想你的验证会简单得多,如果你能证明只要x是mask的子集,然后参考我的答案,
-(x^ mask)-1=~(x^ mask)+1==(x^ mask)+1==(x^ mask)+1==(x^ mask)+1
。最后一个等式成立,因为位集是不相交的,其他的总是正确的(至少在2-补码中)。那些对我得出这个答案所采取的步骤感到好奇的人可以参考。也许值得指出的是,这些都不相同,这通常与做比特旋转的人有关:-虽然哪一个更短似乎取决于编译器。不知道哪一个会更快,或者差异是否显著。2点,很好。然而,我认为这是相同的方法,只是通过零传播借来,而不是通过一传播。@zch,没错,谢谢。只有当x开始时所有的非屏蔽位都清除了,我才会重新表述答案。@Jasen,当然。但设置这些非屏蔽位并不困难。其他答案也有类似的问题。