K&;R C编程语言练习2-9
我不理解练习2-9,用K&R C编程语言, 第2章,第2.10节: 练习2-9。在2的补码系统中,x&=(x-1)删除x中最右边的1位。解释原因。使用此观察可以编写更快版本的bitcount 位计数函数是: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)
/* 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位当成了最右边的位