if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
どうしてこれなの?
(0 & -0) == 0
であるため、説明は完全に正確ではありませんが、0は2の累乗ではありません。それを言うより良い方法は
((n & -n) == n)
nが2の累乗、または2の累乗の負、またはゼロの場合。
Nが2の累乗である場合、バイナリのnは単一の1の後にゼロが続きます。 2の補数の-nは逆+1であるため、ビットは次のように整列します。
n 0000100...000
-n 1111100...000
n & -n 0000100...000
これが機能する理由を理解するには、2の補数を逆+ 1、-n == ~n + 1
と見なします。
n 0000100...000
inverse n 1111011...111
+ 1
two's comp 1111100...000
2の補数を得るために1を追加するときは、1つを最後まで実行するためです。
Nが2の累乗†以外の場合、2の補数はそのキャリーのために設定された最高ビットを持たないため、結果はビットが欠落します。
†-またはゼロまたは2の累乗の負数...上部で説明されているように。
2の補数では、_-n
_は_~n+1
_であるためです。
n
が2の累乗である場合、1ビットしか設定されていません。したがって、_~n
_には、1つを除くすべてのビットが設定されています。 1を追加すると、特別なビットが再度設定され、n & (that thing)
がn
と等しくなるようになります。
そのJavaソースの前の行で0と負の数が除外されているため、逆も当てはまります。 n
に複数のビットが設定されている場合、そのうちの1つがそのような最高のビットです。このビットはnot _+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の場合にのみ、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.
2の累乗の場合のみ、(n & -n)
はnになります。
これは、2の累乗が、ゼロの長い海で単一のセットビットとして表されるためです。否定は、正反対の単一のゼロを生成します(1があった場所で) 1の海で。 1を追加すると、下の方がゼロのあるスペースに移動します。
そしてビット単位と(&)は1を再び除外します。
2の補数表現では、2の累乗のユニークな点は、k番目のビット(n = 2 ^ k)を除いて、すべて0ビットで構成されていることです。
base 2 base 10
000001 = 1
000010 = 2
000100 = 4
...
2の補数で負の値を取得するには、すべてのビットを反転して1つ追加します。 2の累乗の場合、正の値であった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と同じになることが簡単にわかります。
このグラフにない他の値を見ると、これが2の累乗以外には当てはまらない理由がわかります。
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&-nは(n> 0の場合)1ビットのみが設定され、そのビットはnの最下位の設定ビットになります。 2の累乗であるすべての数値の場合、最下位のセットビットが唯一のセットビットです。他のすべての数値については、複数のビットセットがあり、その中で最下位のみが結果に設定されます。
簡単に言うと、nが2の累乗である場合、つまり1ビットのみが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
の2の補数です(つまり、1である唯一のビットはそのまま残り、そのビットの左側のビットは1になります。これは、の結果から実際には問題ではありません。 AND演算子&
2ビットのいずれかがゼロの場合は0になります):
000000...000010000...00000 <<< n
&
111111...111110000...00000 <<< -n
--------------------------
000000...000010000...00000 <<< n
これは2の累乗とその 2の補数 の特性です。
たとえば、8を取ります。
8 = 0b00001000
-8 = 0b11111000
2の補数の計算:
Starting: 0b00001000
Flip bits: 0b11110111 (one's complement)
Add one: 0b11111000
AND 8 : 0b00001000
2の累乗の場合、1ビットのみが設定されますので、追加するとnが発生しますth 2のビットn 設定する(1つはnに運び続けるth ビット)。次に、2つの数値をAND
すると、元の数値が返されます。
2の累乗ではない数値の場合、他のビットは反転されないため、AND
は元の数値を生成しません。
例を通して示されている:
16進数で8 = 0x000008
-8の16進数= 0xFFFFF8
8&-8 = 0x000008