为什么如果(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 ... 

并且因为-nn的二进制补码(这意味着唯一的位1保持原样并且该位的左侧的位被设置为1,这实际上并不重要,因为AND运算符的结果&如果两个位中的一个为零,则将为0):

 000000...000010000...00000 <<< n & 111111...111110000...00000 <<< -n -------------------------- 000000...000010000...00000 <<< n 

通过示例显示:

8在hex= 0x000008

-8hex= 0xFFFFF8

8&-8 = 0x000008