练习2-9.在二进制补码系统中,x &=(x-1)删除x中最右边的1位。解释一下为什么使用这个观察来编写bitcount
的更快版本。
练习中提到的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中最右边的1改变了它在x - 1中的位置,因为减去1,例如如果x = 1010,那么x-1 = 1001,当我们按位与它们时,我们得到1000,所以x中最右边的1消失了 *
下面是我实现这个想法的一个简短版本的bitcount
:
#include <stdio.h>
int bitcount(unsigned x);
int main() {
int x, onebits;
printf("Enter an integer: ");
scanf("%d", &x);
onebits = bitcount(x);
printf("This integer contains %d one bits.", onebits);
return 0;
}
int bitcount(unsigned x) {
int b;
for (b = 0; x != 0; x &= (x - 1))
++b;
return b;
}
如果我犯了错误,有人能纠正我吗?或者提供一个比我更严格的答案?Thanks!:)
这个练习来自里奇和克宁汉的著名著作《C编程语言》。
2条答案
按热度按时间pu3pd22g1#
您的代码很好,但对操作工作原因的解释不是很严谨。
二进制数可以以
0
或1
结尾。如果它以
1
结尾,减去1就可以将其变为0
。当你把这个和原来的数字,所有其他位保持不变,和1 & 0
成为0
。由于最后1位也是最后一位,因此这将删除最后1位。如果它以
0
结尾,减1必须从下一个更高的位“借用”。如果该位也是0
,则从下一位借用,依此类推。所有低阶0位都翻转为1
,当借用结束时,1
位翻转为0
。所以如果你从111000
开始,借用和翻转的结果是110111
。正如您所看到的,所有低阶0
位都变成了1
,最低的1
位变成了0
。当你和这些,比翻转和借用系列高的位保持不变,而低位都抵消了0
。最终结果是最低的1
位被转换为0
。dwbf0jvd2#
我有点直观地理解了为什么会发生这种情况,但我找不到更严格的答案,无论如何,这是我得出的答案:我猜这背后的原因是因为当我们减去1时,我们改变了最右边位的位置,因此当我们逐位与x和x-1时,最右边的1消失了,因为x中最右边的1改变了它在x - 1中的位置,因为减去1,例如如果x = 1010,那么x-1 = 1001,当我们按位与它们时,我们得到1000,所以x中最右边的1消失了
在二进制补码计数系统中,有符号和无符号算术的工作方式相同(算术是相同的,唯一的区别是检测到溢出或进位的点),因此二进制数可以表示为二进制的字符串或1和ceros,通常在右边以0或更多
0
位结束。如果你减去一个数字,数字的末尾有零,所有这些零将被转换为一(使进位传播到下一个数字),最后,最后一个
1
位将被改变为零,这是进位从右边传播的结果。进位一旦从它遇到的第一个数字中减去就停止传播,所以对任何数字的结果操作都是所有尾随的零都变成1,最后一个(只有最后一个)变成0。如果你现在按位和所有这些位,你会看到所有的 equal 位都被
&
运算符保持不变,而不同的位都被清零(1 ^ 0 == 0 ^ 1 == 0)这意味着所有的零都保持不变,最后一个1
位被清零。QED.(你可以检查如果没有零结束的数字,只有最后一位被改变为零)另一方面,这将加速您继续的方式,因为它会立即检测
1
位本身并跳过0
位。对于一个有一个小的1
位集的数字,速度会更快。还有另一种(更快)的方法来计算
1
位:你可以把你的数字看作是一组2位寄存器,提取每个寄存器的左位(这可以并行完成),右移它,并做一个完整的加法到右移,这将在一个镜头中把所有的位对加在一起,给出一组2位的数字(范围为0..2,所以它永远不会溢出),如果你现在重复这个过程,但是考虑到这些位是2位的组,并将数字从2位扩展到4位,我们可以再次把位对的数字加在一起,并继续这样做,直到我们有一个32位的完整寄存器(或64,只是多一步),以寄存器中所有1位的总和结束:这导致了下面的结果(有点晦涩),但比你的循环效率高得多,它有一个更糟糕的最坏情况,当所有的1都必须一个接一个地归零时。在这种方法中,你可以得到平行和的利润。你就开得更快