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