【问题标题】:Can someone explains why this works to count set bits in an unsigned integer?有人可以解释为什么这可以计算无符号整数中的设置位吗?
【发布时间】:2020-01-09 23:02:57
【问题描述】:

我看到这个代码叫做"Counting bits set, Brian Kernighan's way"。我很困惑如何“按位与”整数及其减量来计算设置位,有人可以解释一下吗?

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

【问题讨论】:

  • 您是否尝试过用笔和纸逐步完成该过程,看看会发生什么?

标签: c bit-manipulation kernighan-and-ritchie


【解决方案1】:

演练

让我们通过一个示例来遍历循环:让我们将v = 42 设置为二进制的0010 1010

  • 第一次迭代c=0, v=42 (0010 1010)。 现在v-141,它是二进制的0010 1001。 让我们计算v & v-1

       0010 1010
     & 0010 1001
       .........
       0010 1000
    

    现在v&v-1 的值是二进制的0010 1000 或十进制的40。该值存储到v

  • 第二次迭代c=1, v=40 (0010 1000)。现在v-139,它是二进制的0010 0111。让我们计算v & v-1

       0010 1000
     & 0010 0111
       .........
       0010 0000
    

    现在v&v-1 的值是0010 0000,即十进制的32。此值存储到v

  • 第三次迭代c=2, v=32 (0010 0000)。现在v-131,它是二进制的0001 1111。让我们计算v & v-1

       0010 0000
     & 0001 1111
       .........
       0000 0000
    

    现在v&v-1 的值是0

  • 第四次迭代c=3, v=0循环终止c 包含3,这是42 中设置的位数。

为什么会起作用

您可以看到v-1 的二进制表示将最低有效位或 LSB(即最右边的位为 1)从 1 设置为 0,并且所有位都位于 LSB 的右侧从 0 到 1。

当您在vv-1 之间执行按位与 时,在vv-1 中,从 LSB 剩下的位相同,因此按位与将保持不变. LSB 右边的所有位(包括 LSB 本身)都是不同的,因此结果位将为 0。

在我们最初的v=42 (0010 1010) 示例中,LSB 是右起第二位。您可以看到v-142 具有相同的位,除了最后两个:0 变为 1,1 变为 0。

同样,v=40 (0010 1000) LSB 是右数第四位。在计算v-1 (0010 0111)时,可以看到左四位保持不变,而右四位反转(零变成一,一变成零)。

因此v = v & v-1 的作用是将v 的最低有效位设置为0,其余保持不变。当所有位都以这种方式被清除后,v 为 0,我们已经对所有位进行了计数。

【讨论】:

  • 非常遗憾的是,在如此好的 C 语言答案中,仅仅因为 come later 而没有得到正确的奖励(这是因为答案试图写出完整的答案)。支持,因为有时它也会发生在我身上。
  • 谢谢。这也适用于稍后将搜索相同答案的下一个人:)
【解决方案2】:

每次循环计数一位,并清除一位(设置为零)。

它的工作原理是:当您从一个数字中减去 1 时,您会将最低有效位更改为 0,将更不重要的位更改为 1——尽管这无关紧要。没关系,因为它们在您要递减的值中为零,因此无论如何在与操作之后它们都将为零。

XXX1 => XXX0
XX10 => XX01
X100 => X011
etc.

【讨论】:

  • 所以递减的值正在清除最低有效的设置位,而未递减的值充当该位的位掩码?
  • 我会说减少的值是要删除的位的掩码。该掩码中的较高位与原始值中的位匹配,掩码中的较低位是“无关紧要的”,因为我们知道它们在原始值中已经为零。
【解决方案3】:

A=an-1an-2...a1a0 sub> 是我们要计算位的数字,k 是最右边位的索引。

因此 A=an-1an-2...ak+1100...0 =Ak+2k 其中 Ak=an-1an-2...ak+1000...0

作为 2k-1=000..0111..11,我们有 A-1=Ak+2k-1=an-1an-2...a k+1011...11

现在执行 AA-1

的按位 &

an-1an-2...ak+1100...0 A
an-1an-2...ak+1011...1 A-1
an-1an-2.. .ak+1000...0 A&A-1=Ak

所以A&A-1A是一样的,只是它的最右边的位被清除了,这证明了方法的有效性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多