K&;R C编程语言练习2-9

K&;R C编程语言练习2-9,c,C,我不理解练习2-9,用K&R C编程语言, 第2章,第2.10节: 练习2-9。在2的补码系统中,x&=(x-1)删除x中最右边的1位。解释原因。使用此观察可以编写更快版本的bitcount 位计数函数是: /* bitcount: count 1 bits in x */ int bitcount(unsigned x) { int b; for (b = 0; x != 0; x >>= 1) if (x & 01)

我不理解练习2-9,用K&R C编程语言, 第2章,第2.10节:

练习2-9。在2的补码系统中,x&=(x-1)删除x中最右边的1位。解释原因。使用此观察可以编写更快版本的bitcount

位计数函数是:

/* bitcount: count 1 bits in x */

int bitcount(unsigned x)
{
    int b;
    for (b = 0; x != 0; x >>= 1)
        if (x & 01)
            b++;
    return b;
}
该函数在检查是否为位1后删除最右边的位,然后弹出最后一位

我不明白为什么
x&(x-1)
会删除最右边的1位? 例如,假设x是二进制的
1010
,x-1是
1001
,而
x&(x-1)
1011
,那么最右边的位就在那里,应该是一位,我错在哪里

另外,练习中提到了二的补语,是否与这个问题有关

非常感谢

为什么
x&(x-1)
删除最右边的订单位?试试看:

如果最严格的顺序位是1,x有一个二进制表示形式
a…b1
x-1
a…b0
,因此按位and将给出
a…b1
,因为公共位由
保持不变,
1&0
0

Else x的二进制表示形式是a…b10…0
x-1
a…b01…1
并且由于与上面相同的原因
x&(x-1)
将是
a…b00…0
再次清除最右边的顺序位

因此,不必扫描所有位来找出哪个位是0,哪个位是1,只需迭代操作
x=x&(x-1)
直到x为0:步数将是1位的数目。它比朴素的实现更有效,因为从统计上讲,您将使用一半的步骤

代码示例:

int bitcount(unsigned int x) {
    int nb = 0;
    while (x != 0) {
        x &= x-1;
        nb++
    }
    return nb;
}

首先,你需要相信K&R是正确的。 第二,你可能对单词有一些误解

让我再为你澄清一下。最右边的1位不是指最右边的位,而是最右边的位,即二进制形式的1

让我们任意假设x是xxxxxxx 1000(x可以是0或1)。然后从右到左,第四位是“最右边的1位”。基于这种理解,让我们继续讨论这个问题

为什么x&=(x-1)可以删除最右边的1位

在二的补码系统中,-1用全部1位模式表示

所以x-1实际上是x+(-1),也就是xxxxxxx 1000+11111。棘手的问题来了

在最右边的1位之前,所有0变为1,最右边的1位变为0,进位1转到左边。这个1将继续向最左边移动并导致溢出,同时,所有的“x”位仍然是一个,因为“x”+“1”+“1”(进位)导致一个“x”位

然后x&(x-1)将删除最右边的1位

希望你现在能理解


谢谢。

这里有一个简单的解释方法。让我们任意假设数字Y是xxxxxxx 1000(x可以是0或1)

xxxxxxx 1000-1=xxxxxxx 0111

xxxxxxx 1000&xxxxxxx 0111=xxxxxxx 0000(请参阅,“最右边的1”已消失。)


因此Y变为0之前Y&=(Y-1)的重复次数将是Y中1的总数。

1001&1010
不是
1011
。您想到的是二进制或,而不是二进制和。检查您对(“按位”)的“逻辑”操作的理解:
1011
1010
1001
的“逻辑或”(
|
)(结果是每个位置的
1
);“逻辑AND”(&)是
1000
。请尝试格式化(缩进)您的代码段。我完全错把最右边的1位当成了最右边的位