Строка 294 источника java.util.Random говорит
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
Почему это?
Строка 294 источника java.util.Random говорит
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
Почему это?
(n & (n - 1)) == 0
также работает (она удаляет бит самого низкого порядка, если битов не осталось, значит, изначально был установлен не более 1 бит).
Ответы:
Описание не совсем точное, потому что (0 & -0) == 0
0 - это не степень двойки. Лучший способ сказать это
((n & -n) == n)
когда n является степенью двойки, или отрицательной величиной степени двойки, или нулем.
Если n является степенью двойки, то n в двоичном формате представляет собой единицу с нулями. -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 было чем-то другим, кроме степени двойки †, тогда в результате был бы пропущен бит, потому что в дополнительном двоичном коде не был бы установлен самый высокий бит из-за этого переноса.
† - или ноль, или отрицательное значение степени двойки ... как объяснено вверху.
(0 & -0) == 0
, непосредственно предшествующего заявления является if (n <= 0) throw ...
. Это означает, что в этот момент тестируемое число никогда не будет 0 (или отрицательным).
Random.java
который я не читал.
n
; Я не проверял это предположение, но почему-то сомневаюсь, что a double
будет вести себя так же.
n
поскольку в этом вопросе есть тег "java". &
не определен на Java double
или float
в Java. Он определен только для целочисленных типов и логических значений. Поскольку -
не определено для логических значений, мы можем с уверенностью сделать вывод, что n
является целым.
Поскольку в дополнении до 2, -n
это~n+1
.
Если n
это степень 2, то у него установлен только один бит. Итак ~n
, все биты установлены, кроме этого. Добавьте 1, и вы снова установите специальный бит, убедившись, что n & (that thing)
он равенn
.
Обратное также верно, потому что 0 и отрицательные числа были исключены предыдущей строкой в этом исходном коде Java. Если 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, выйдет 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.
В представлении дополнения до двух уникальность степеней двойки состоит в том, что они состоят из всех 0 битов, кроме k-го бита, где n = 2 ^ k:
base 2 base 10
000001 = 1
000010 = 2
000100 = 4
...
Чтобы получить отрицательное значение в дополнении до двух, вы переворачиваете все биты и добавляете единицу. Для степени двойки это означает, что вы получаете набор единиц слева до бита 1, который был положительным значением включительно, а затем набор нулей справа:
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.
Если вы посмотрите на другие значения, отсутствующие в этой диаграмме, вы увидите, почему это не относится ни к чему, кроме степени двойки:
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. Для всех чисел, являющихся степенями двойки, единственный установленный бит является младшим значащим битом. Для всех других чисел существует более одного набора битов, из которых в результате будет установлен только наименее значимый.
Это свойство степеней двойки и их дополнения до двух. .
Например, возьмите 8:
8 = 0b00001000
-8 = 0b11111000
Вычисление двух дополнений:
Starting: 0b00001000
Flip bits: 0b11110111 (one's complement)
Add one: 0b11111000
AND 8 : 0b00001000
Для степеней двойки будет установлен только один бит, поэтому добавление приведет к установке n- го бита 2 n (один продолжает переноситься на n- й бит). Тогда, когда тыAND
наберете два числа, вы получите оригинал обратно.
Для чисел, которые не являются степенью двойки, другие биты не будут перевернуты, поэтому 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
это дополнение до 2 n
(это означает, что единственный бит, который равен 1, остается таким, как есть, а биты с левой стороны этого бита сидят на 1, что на самом деле не имеет значения, поскольку результат оператора AND &
будет 0, если один из двух бит равен нулю):
000000...000010000...00000 <<< n
&
111111...111110000...00000 <<< -n
--------------------------
000000...000010000...00000 <<< n