问题描述
摘自 Hacker's Delight:2nd Edition :
这里的公式似乎有点尴尬。当 x 小于时,如何从 1 向量(大概为 0x1111 1111 )中减去某些 x 向量> 1 ? (例如:(如示例中所示) 0x0101 1000 - 0x0000 0000 对我来说没有任何意义)前者比第一个数字小,单词少也不存储带符号的向量。这与RISC有关吗?
如书中“注释”部分所指定。粗体字母表示单词 x = 00000000的矢量。而粗体字母则不同于浅色的面孔1。粗体 1 = 11111111是8位单词。
Edit2 :特别感谢Paul Hankin找出了此处使用的非常规符号。粗体字表示32位大小的字,即[00000001],而浅色表示1则表示与C中的数字1。
解决方法
我们来看看x-1
的作用。
假设x
是值'???? 1000
(?是0还是1)=> x-1 = ???? 0111
=> x & (x-1) = ???? 0000
无论最右边的1放在x
中的哪个位置,这都非常相似。
请求的示例:x=00001111
=> x-1=00001110
=> x & (x-1) = 00001110
P.s。 x-1 = 00001110 - 00000001 (<=> 00001110 + 11111111)
减去1
由于我们对十进制的理解要比对二进制的更为熟悉,所以有时它有助于了解十进制会发生什么。
在十进制中减去1会发生什么?以1786000 - 1 = 1785999
为例。
如果从十进制正数1
中减去x
:
-
x
右边的所有零变为9
; -
x
的最右边的非零数字减1; - 其他数字不受影响。
现在,以二进制形式,它的工作原理完全相同,只是我们只有0 1
而不是0 123456789
。
如果从二进制数1
中减去x
,则
-
x
右边的所有零变为1
; -
x
的最右边非零位变为0
; - 其他位不受影响。
负数呢?令人高兴的是,使用2的补码表示负数的行为就像正数。实际上,当查看x
的位时,可以从1
中减去x
,而无需知道x
是有符号整数还是无符号整数。>
x & (x-1)
让我们从一个示例开始:x = 01011000
。我们可以按照我刚才解释的方式减去1:
x = 01011000
x-1 = 01010111
现在按位和操作x & (x-1)
的结果是什么?我们在每一列中取两位;如果它们都为1,则我们写1;如果其中至少一个为0,则我们写为0。
x = 01011000
x-1 = 01010111
x&(x-1) = 01010000
发生了什么事?
-
x
右边的所有零都保持为零; -
x
中最右边的1由于x-1
而变成0; - 所有其他位均不受影响,因为它们在
x
和x-1
中是相同的。
结论:我们已将x
的最右边1置零,而所有其他位均不受影响。