为什么如果(n和-n)== n那么n是2的幂?
java.util.Random源代码行294说
if ((n & -n) == n) // ie, n is a power of 2 // rest of the code
为什么是这样?
描述不完全准确,因为(0 & -0) == 0
但0不是2的幂。 更好的方式是说
((n & -n) == n)
当n是2的幂,或者2的幂的负,或者0。
如果n是2的幂,那么二进制中的n是单个1,后面是零。 -n在二进制补码中是反+ 1,所以比特排列如此
n 0000100...000 -n 1111100...000 n & -n 0000100...000
要明白为什么这个工作,考虑二进制补码为逆+ 1, -n == ~n + 1
n 0000100...000 inverse n 1111011...111 + 1 two's comp 1111100...000
因为你在加一个来获得二进制补码的时候一直通过这个。
如果n不是2的幂,那么结果将会丢失一点,因为二进制补码不会由于该进位而被设置为最高位。
† – 或者是0或者2的幂的负数…如顶部所解释的那样。
因为在2的补码中, -n
是~n+1
。
如果n
是2的幂,那么它只设置一个位。 所以~n
除了一个之外都设置了所有的位。 加1,再次设置特殊位,确保n & (that thing)
等于n
。
反过来也是如此,因为在这个Java源代码中,前面一行排除了0和负数。 如果n
有一个以上的比特集,那么其中的一个就是最高的那个比特。 这个位不会被+1
设置,因为有一个较低的清除位来“吸收”它:
n: 00001001000 ~n: 11110110111 -n: 11110111000 // the first 0 bit "absorbed" the +1 ^ | (n & -n) fails to equal n at this bit.
您需要将这些值视为位图来查看原因:
1 & 1 = 1 1 & 0 = 0 0 & 1 = 0 0 & 0 = 0
所以只有两个字段都是1才会出来
现在-n做了2的补充。 它将所有0
更改为1
,它将添加1。
7 = 00000111 -1 = NEG(7) + 1 = 11111000 + 1 = 11111001
然而
8 = 00001000 -8 = 11110111 + 1 = 11111000 00001000 (8) 11111000 (-8) --------- & 00001000 = 8.
(n & -n)
是n。
这是因为2的幂在0的长海中表示为一个单一的位。 否定将产生完全相反的结果, 在1的海中有一个零(在 1的地方)。 加1会将低位移到零所在的位置。
并按位(&)将再次过滤掉1。
在二进制补码表示中,关于两个幂的独特之处在于它们由全部0个比特组成,除了第k个比特,其中n = 2 ^ k:
base 2 base 10 000001 = 1 000010 = 2 000100 = 4 ...
为了在二进制补码中得到负值,可以翻转所有的位并添加一个。 对于二的幂,这就意味着你在左边得到一堆1,包括正值的1位,然后右边的一堆0:
n base 2 ~n ~n+1 (-n) n&-n 1 000001 111110 111111 000001 2 000010 111101 111110 000010 4 000100 111011 111100 000100 8 001000 110111 111000 001000
您可以很容易地看到第2和第4列的结果将与第2列相同。
如果你看看这个图表中缺less的其他值,你可以看到为什么这不适用于除了两个的权力:
n base 2 ~n ~n+1 (-n) n&-n 1 000001 111110 111111 000001 2 000010 111101 111110 000010 3 000011 111100 111101 000001 4 000100 111011 111100 000100 5 000101 111010 111011 000001 6 000110 111001 111010 000010 7 000111 111000 111001 000001 8 001000 110111 111000 001000
对于n> 0,n&n将只有1位置位,并且该位将是n中最不重要的置位位。 对于所有数字是2的幂,最不重要的设置位是唯一的设置位。 对于所有其他数字,有多个位集,其中只有最不重要的将被设置在结果中。
它是2和2的幂的性质。
例如,拿8:
8 = 0b00001000 -8 = 0b11111000
计算二进制补码:
Starting: 0b00001000 Flip bits: 0b11110111 (one's complement) Add one: 0b11111000 AND 8 : 0b00001000
对于2的幂,只有一个位将被设置,因此join将导致2 n的 第 n位被设置(该位持续到第n位)。 然后当你AND
这两个数字,你得到原来的回来。
对于不是2的幂的数字,其他位不会翻转,所以AND
不会产生原始数字。
简单地说,如果n是2的幂,那么意味着只有一个比特被设置为1,而其他的则为0:
00000...00001 = 2 ^ 0 00000...00010 = 2 ^ 1 00000...00100 = 2 ^ 2 00000...01000 = 2 ^ 3 00000...10000 = 2 ^ 4 and so on ...
并且因为-n
是n
的二进制补码(这意味着唯一的位1保持原样并且该位的左侧的位被设置为1,这实际上并不重要,因为AND运算符的结果&
如果两个位中的一个为零,则将为0):
000000...000010000...00000 <<< n & 111111...111110000...00000 <<< -n -------------------------- 000000...000010000...00000 <<< n
通过示例显示:
8在hex= 0x000008
-8hex= 0xFFFFF8
8&-8 = 0x000008