La riga 294 della fonte java.util.Random dice
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
Perchè è questo?
La riga 294 della fonte java.util.Random dice
if ((n & -n) == n) // i.e., n is a power of 2
// rest of the code
Perchè è questo?
(n & (n - 1)) == 0
funziona anche (rimuove il bit di ordine più basso, se non ci sono bit rimasti allora c'era al massimo 1 bit impostato al primo posto).
Risposte:
La descrizione non è del tutto accurata perché (0 & -0) == 0
ma 0 non è una potenza di due. Un modo migliore per dirlo
((n & -n) == n)
quando n è una potenza di due, o il negativo di una potenza di due, o zero.
Se n è una potenza di due, allora n in binario è un singolo 1 seguito da zeri. -n in complemento a due è l'inverso + 1 quindi i bit si allineano così
n 0000100...000
-n 1111100...000
n & -n 0000100...000
Per capire perché questo lavoro, considera il complemento a due come inverso + 1, -n == ~n + 1
n 0000100...000
inverse n 1111011...111
+ 1
two's comp 1111100...000
dal momento che porti l'uno fino in fondo quando ne aggiungi uno per ottenere il complemento a due.
Se n fosse qualcosa di diverso da una potenza di due †, il risultato mancherebbe un po 'perché il complemento a due non avrebbe il bit più alto impostato a causa di quel riporto.
† - o zero o un negativo di una potenza di due ... come spiegato in alto.
(0 & -0) == 0
, l'affermazione immediatamente precedente è if (n <= 0) throw ...
. Significa che il numero sotto test non sarà mai 0 (o negativo) a quel punto.
Random.java
che non ho letto.
n
sia; Non ho verificato questa ipotesi, ma in qualche modo dubito che a double
si comporterebbe allo stesso modo.
n
poiché questa domanda ha il tag "java". &
non è definito su double
o float
in Java. È definito solo su tipi interi e booleano. Poiché -
non è definito per valori booleani, possiamo dedurre con sicurezza che n
sia integrale.
Perché nel complemento di 2, -n
è ~n+1
.
Se n
è una potenza di 2, ha solo un bit impostato. Quindi ~n
ha tutti i bit impostati tranne quello. Aggiungi 1 e imposti di nuovo il bit speciale, assicurandoti che n & (that thing)
sia uguale a n
.
Il contrario è vero anche perché 0 e numeri negativi sono stati esclusi dalla riga precedente in quella sorgente Java. Se n
ha più di un bit impostato, allora uno di questi è il bit più alto. Questo bit non verrà impostato da +1
perché c'è un bit di clear più basso per "assorbirlo":
n: 00001001000
~n: 11110110111
-n: 11110111000 // the first 0 bit "absorbed" the +1
^
|
(n & -n) fails to equal n at this bit.
È necessario guardare i valori come bitmap per vedere perché questo è vero:
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
Quindi solo se entrambi i campi sono 1 verrà visualizzato un 1.
Ora -n fa un complemento di 2. Cambia tutto 0
a 1
e aggiunge 1.
7 = 00000111
-1 = NEG(7) + 1 = 11111000 + 1 = 11111001
però
8 = 00001000
-8 = 11110111 + 1 = 11111000
00001000 (8)
11111000 (-8)
--------- &
00001000 = 8.
Solo per potenze di 2 sarà (n & -n)
n.
Questo perché una potenza di 2 è rappresentata come un singolo bit impostato in un lungo mare di zero. La negazione produrrà l'esatto opposto, un unico zero (nel punto in cui si trovava l'1) in un mare di 1. L'aggiunta di 1 sposterà quelli inferiori nello spazio in cui si trova lo zero.
E bit a bit e (&) filtreranno nuovamente l'1.
Nella rappresentazione in complemento a due, l'unica cosa sulle potenze di due, è che consistono di tutti i bit 0, ad eccezione del k-esimo bit, dove n = 2 ^ k:
base 2 base 10
000001 = 1
000010 = 2
000100 = 4
...
Per ottenere un valore negativo in complemento a due, capovolgi tutti i bit e ne aggiungi uno. Per potenze di due, ciò significa che ottieni un gruppo di 1 a sinistra fino a includere l'1 bit che era nel valore positivo, e poi un gruppo di 0 a destra:
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
Puoi facilmente vedere che il risultato delle colonne 2 e 4 sarà lo stesso della colonna 2.
Se guardi gli altri valori mancanti da questo grafico, puoi capire perché questo non vale per nient'altro che i poteri di due:
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 (per n> 0) avrà sempre e solo 1 bit impostato, e quel bit sarà il bit impostato meno significativo in n. Per tutti i numeri che sono potenze di due, il bit impostato meno significativo è l'unico bit impostato. Per tutti gli altri numeri, è impostato più di un bit, di cui solo il meno significativo verrà impostato nel risultato.
È proprietà dei poteri di 2 e del loro complemento a due .
Ad esempio, prendi 8:
8 = 0b00001000
-8 = 0b11111000
Calcolo del complemento a due:
Starting: 0b00001000
Flip bits: 0b11110111 (one's complement)
Add one: 0b11111000
AND 8 : 0b00001000
Per potenze di 2, verrà impostato un solo bit, quindi aggiungendo verrà impostato l' ennesimo bit di 2 n (quello continua a portare all'ennesimo bit). Poi, quando hai AND
i due numeri, riavrai l'originale.
Per i numeri che non sono potenze di 2, gli altri bit non verranno capovolti, quindi AND
non restituirà il numero originale.
Semplicemente, se n è una potenza di 2 significa che solo un bit è impostato a 1 e gli altri sono a 0:
00000...00001 = 2 ^ 0
00000...00010 = 2 ^ 1
00000...00100 = 2 ^ 2
00000...01000 = 2 ^ 3
00000...10000 = 2 ^ 4
and so on ...
e poiché -n
è un complemento di 2 di n
(ciò significa che l'unico bit che è 1 rimane com'è ei bit sul lato sinistro di quel bit sono posti a 1 che in realtà non ha importanza poiché il risultato dell'operatore AND &
sarà 0 se uno dei due bit è zero):
000000...000010000...00000 <<< n
&
111111...111110000...00000 <<< -n
--------------------------
000000...000010000...00000 <<< n