Gli operatori bit a bit sono quegli operatori dall'aspetto strano che potrebbero sembrare difficili da capire ... ma non più! Questo articolo facile da seguire ti aiuterà a capire cosa sono e come usarli, con un paio di esempi pratici e per mostrarti quando e perché avresti bisogno di loro.
Gli operatori bit a bit sono operatori (come +, *, &&, ecc.) Su cui operano int
e uints
a livello binario. Ciò significa che guardano direttamente alle cifre binarie o ai bit di un intero. Tutto questo sembra spaventoso, ma in verità gli operatori bit a bit sono abbastanza facili da usare e anche abbastanza utili!
È importante, tuttavia, che tu abbia una comprensione dei numeri binari e dei numeri esadecimali. Se non lo fai, consulta questo articolo: ti aiuterà davvero! Di seguito è una piccola applicazione che ti permetterà di provare i diversi operatori bit a bit.
Non preoccuparti se non capisci cosa sta succedendo, sarà tutto chiaro presto ...
Diamo un'occhiata agli operatori bit a bit forniti da AS3. Molte altre lingue sono abbastanza simili (ad esempio, JavaScript e Java hanno operatori praticamente identici):
Ci sono un paio di cose che dovresti prendere da questo: in primo luogo, alcuni operatori bit a bit sembrano simili agli operatori che hai usato prima (& vs. &&, | vs. ||). Questo perché loro siamo un po 'simile.
In secondo luogo, la maggior parte degli operatori bit a bit ha un modulo di assegnazione composto di se stessi. Questo è lo stesso di come puoi usare + e + =, - e - =, ecc.
Innanzitutto: l'operatore AND bit a bit, &. Un rapido heads-up però: normalmente, int
e uints
occupare 4 byte o 32 bit di spazio. Questo significa ciascuno int
o uint
è memorizzato come 32 cifre binarie. Per il bene di questo tutorial, faremo finta che a volte int
e uints
richiede solo 1 byte e ha solo 8 cifre binarie.
L'operatore & confronta ogni cifra binaria di due numeri interi e restituisce un nuovo numero intero, con un 1 in cui entrambi i numeri hanno un 1 e uno 0 in qualsiasi altro luogo. Un diagramma vale più di mille parole, quindi ecco uno per chiarire le cose. Rappresenta il fare 37 e 23
, quale è uguale a 5
.
Si noti come ciascuna cifra binaria di 37 e 23 viene confrontata, e il risultato ha un 1 ovunque sia il 37 che il 23 hanno un 1, e il risultato ha uno 0 altrimenti.
Un modo comune di pensare alle cifre binarie è come vero
o falso
. Cioè, 1 è equivalente a vero
e 0 è equivalente a falso
. Questo rende l'operatore & più sensato.
Quando confrontiamo due booleani, normalmente lo facciamo boolean1 && boolean2
. Quell'espressione è vera solo se entrambi Boolean1
e boolean2
sono veri Nello stesso modo, intero1 e intero2
è equivalente, poiché l'operatore & produce solo un 1 quando entrambe le cifre binarie dei nostri due numeri interi sono 1.
Ecco una tabella che rappresenta quell'idea:
Un piccolo uso dell'operatore & è di controllare se un numero è pari o dispari. Per gli interi, possiamo semplicemente controllare il bit più a destra (chiamato anche il bit meno significativo) per determinare se il numero intero è pari o dispari. Questo perché durante la conversione in base 10, il bit più a destra rappresenta 20 o 1. Quando il bit più a destra è 1, sappiamo che il nostro numero è strano poiché aggiungiamo 1 a un gruppo di poteri di due che sarà sempre pari. Quando il bit più a destra è 0, sappiamo che il nostro numero sarà pari, poiché consiste semplicemente nell'aggiungere un gruppo di numeri pari.
Ecco un esempio:
var randInt: int = int (Math.random () * 1000); if (randInt & 1) trace ("Odd number."); else trace ("Even number.");
Sul mio computer, questo metodo era circa il 66% più veloce rispetto all'utilizzo randInt% 2
controllare i numeri pari e dispari. Questo è piuttosto un aumento delle prestazioni!
Il prossimo è l'operatore OR bit a bit, |. Come avrai intuito, il | l'operatore è il || operatore come operatore & è per l'operatore &&. Il | l'operatore confronta ogni cifra binaria su due numeri interi e restituisce un 1 se o di questi sono 1. Ancora, questo è simile al || operatore con booleani.
Diamo un'occhiata allo stesso esempio di prima, eccetto ora usando il | operatore al posto dell'operatore &. Ora stiamo facendo 37 | 23
che è uguale a 55:
Possiamo approfittare di & e | | operatori per permetterci di passare più opzioni a una funzione in un singolo int
.
Diamo un'occhiata a una possibile situazione. Stiamo costruendo una classe di finestre pop-up. In fondo, possiamo avere un pulsante Sì, No, OK o Annulla o qualsiasi combinazione di questi - come dovremmo fare questo? Ecco il modo difficile:
public class PopupWindow estende Sprite // Variables, Constructor, etc ... public static void showPopup (yesButton: Boolean, noButton: Boolean, okayButton: Boolean, cancelButton: Boolean) if (yesButton) // aggiungi il pulsante YES if (noButton ) // aggiungi NO Button // e così via per il resto dei pulsanti
È orribile? No. Ma è male, se sei un programmatore, devi cercare l'ordine degli argomenti ogni volta che chiami la funzione. È anche fastidioso - per esempio, se vuoi solo mostrare il pulsante Annulla, devi impostare tutti gli altri booleani
a falso
.
Usiamo ciò che abbiamo imparato su & e | | per fare una soluzione migliore:
public class PopupWindow estende Sprite const statico pubblico YES: int = 1; const statico pubblico NO: int = 2; const statico pubblico OKAY: int = 4; public static const CANCEL: int = 8; public static void showPopup (buttons: int) if (buttons & YES) // aggiungi il pulsante YES if (buttons & NO) // aggiungi il pulsante NO
Come fa un programmatore a chiamare la funzione in modo da mostrare il pulsante Sì, il pulsante No e il pulsante Annulla? Come questo:
PopupWindow.show (PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL);
Cosa sta succedendo? È importante notare che le nostre costanti nel secondo esempio sono tutte potenze di due. Quindi, se osserviamo i loro moduli binari, noteremo che tutti hanno una cifra uguale a 1 e il resto uguale a 0. In realtà, ognuno di essi ha una cifra diversa uguale a 1. Ciò significa che non importa come combiniamo con loro, ogni combinazione ci darà un numero unico. Guardandolo in un modo diverso, fuori dal risultato del nostro | la dichiarazione sarà un numero binario con un 1 ogni volta che le nostre opzioni hanno un 1.
Per il nostro esempio attuale abbiamo PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL
che è equivalente a 1 | 2 | 8
che è riscritto in binario 00000001 | 00000010 | 00001000
che ci dà un risultato di 00001011
.
Ora, nel nostro ShowPopup ()
funzione, usiamo e per controllare quali opzioni sono state inoltrate. Ad esempio, quando controlliamo pulsanti e SÌ
, tutti i bit in SÌ sono uguali a 0 tranne quello molto più a destra. Quindi, stiamo essenzialmente controllando se il bit più a destra nei pulsanti è un 1 o meno. Se è, pulsanti e SÌ
non sarà uguale a 0 e qualsiasi cosa nell'istruzione if verrà eseguita. Viceversa, se il bit più a destra nei pulsanti è 0, pulsanti e SÌ
sarà uguale a 0 e l'istruzione if non verrà eseguita.
L'operatore NOT bit a bit è leggermente diverso da quello che abbiamo visto finora. Invece di prendere un numero intero su ciascun lato, prende un numero intero solo dopo di esso. Questo è proprio come il! operatore, e, non sorprendentemente, fa una cosa simile. In effetti, proprio come! lancia un booleano da vero
a falso
o viceversa, l'operatore ~ inverte ogni cifra binaria in un numero intero: da 0 a 1 e da 1 a 0:
Un rapido esempio. Supponiamo di avere il numero intero 37 o 00100101. ~ 37 è quindi 11011010. Qual è il valore di base 10 di questo? Bene…
uint
vs. int
, e altro ancora!Ora inizia il divertimento! Daremo un'occhiata più da vicino ai numeri binari su un computer. Iniziamo con uint
. Come accennato prima, a uint
è in genere 4 byte o 32 bit, il che significa che ha 32 cifre binarie. Questo è facile da capire: per ottenere il valore di base 10 è sufficiente convertire il numero in base 10 regolarmente. Otterremo sempre un numero positivo.
Ma per quanto riguarda il int
? Utilizza anche 32 bit, ma come memorizza i numeri negativi? Se hai indovinato che la prima cifra è utilizzata per memorizzare il segno, sei sulla strada giusta. Diamo un'occhiata al complemento a due sistema per memorizzare numeri binari. Mentre non entreremo in tutti i dettagli qui, viene utilizzato un sistema a complemento a due perché rende facile l'aritmetica binaria.
Per trovare il complemento a due di un numero binario, basta semplicemente capovolgere tutti i bit (cioè, fare ciò che fa l'operatore ~) e aggiungerne uno al risultato. Proviamo una volta:
Quindi definiamo il nostro risultato come valore -37. Perché questo processo complicato e non solo capovolgi il primo bit e lo chiamiamo -37?
Bene, prendiamo una semplice espressione 37 + -37
. Sappiamo tutti che questo dovrebbe essere uguale a 0, e quando aggiungiamo il 37 al suo complemento a due, questo è ciò che otteniamo:
Si noti che poiché i nostri numeri interi contengono solo otto cifre binarie, l'1 nel nostro risultato è sceso e finiamo con 0, come dovremmo.
Per ricapitolare, per trovare il negativo di un numero, prendiamo semplicemente il suo complemento a due. Possiamo farlo invertendo tutti i bit e aggiungendone uno.
Vuoi provare da solo? Inserisci traccia (~ 37 + 1);
in un file AS3, quindi compilarlo ed eseguirlo. Vedrai che -37 è stampato, come dovrebbe essere.
C'è anche una piccola scorciatoia per farlo a mano: a partire da destra, lavorare a sinistra fino a raggiungere 1. 1. Capovolgere tutti i bit a sinistra di questo primo 1.
Quando guardiamo un numero binario firmato (in altre parole, uno che può essere negativo, an int
non a uint
), possiamo guardare la cifra più a sinistra per dire se è negativa o positiva. Se è uno 0, allora il numero è positivo e possiamo convertirlo in base 10 semplicemente calcolando il suo valore di base 10. Se il bit più a sinistra è un 1, il numero è negativo, quindi prendiamo il complemento a due del numero per ottenere il suo valore positivo e quindi aggiungiamo semplicemente un segno negativo.
Ad esempio, se abbiamo 11110010, sappiamo che è un numero negativo. Possiamo trovare il complemento a due sfogliando tutte le cifre a sinistra dell'estrema destra 1, dandoci 00001110. Questo equivale a 13, quindi sappiamo 11110010 equivale a -13.
Siamo tornati agli operatori bit a bit, e il prossimo è l'operatore XOR bit a bit. Non esiste un operatore booleano equivalente a questo.
L'operatore ^ è simile a & e | operatori in quanto ci vuole un int
o uint
su entrambi i lati. Quando calcola il numero risultante, confronta nuovamente le cifre binarie di questi numeri. Se uno o l'altro è un 1, inserirà un 1 in al risultato, altrimenti inserirà uno 0. Qui è dove il nome XOR, o "esclusivo o" viene da.
Diamo un'occhiata al nostro solito esempio:
L'operatore ^ ha degli usi - è particolarmente utile per la commutazione di cifre binarie - ma non copriremo alcuna applicazione pratica in questo articolo.
Ora siamo sugli operatori di bitshift, in particolare l'operatore di spostamento a sinistra bit a bit qui.
Questi funzionano in modo leggermente diverso rispetto a prima. Invece di confrontare due interi come &, |, e ^, questi operatori spostano un intero. Sul lato sinistro dell'operatore è il numero intero che viene spostato e sulla destra è quanto passare. Quindi, per esempio, 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.
Diamo un'occhiata a questo esempio (ricordate, stiamo solo facendo finta che gli interi abbiano solo 8 bit invece di 32). Qui abbiamo il numero 37 seduto sul suo bel blocco di memoria largo 8 bit.
Bene, facciamo scorrere tutte le cifre verso sinistra di 3, come 37 << 3
farebbe:
Ma ora abbiamo un piccolo problema: cosa facciamo con i 3 bit di memoria aperti da cui abbiamo spostato le cifre?
Ovviamente! Eventuali punti vuoti vengono semplicemente sostituiti con 0. Finiamo con 00101000. E questo è tutto ciò che c'è a sinistra. Tieni presente che Flash pensa sempre che il risultato di un bithift sinistro sia un int
, non a uint
. Quindi se hai bisogno di un uint
per qualche motivo, dovrai lanciarlo su a uint
come questo: uint (37 << 3)
. Questo casting non cambia in realtà nessuna delle informazioni binarie, solo come Flash lo interpreta (l'intero complemento dei due).
Una caratteristica interessante del bitshift di sinistra è che è lo stesso di moltiplicare un numero per due alla potenza shift-mount-th. Così, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8
. Se puoi usare il turno di sinistra invece di Math.pow
, vedrai un enorme aumento delle prestazioni.
Potresti aver notato che il numero binario con cui abbiamo finito non era uguale a 37 * 8. Questo deriva dal nostro uso di soli 8 bit di memoria per interi; se lo provi in ActionScript, otterrai il risultato corretto. Oppure, provalo con la demo nella parte superiore della pagina!
Ora che comprendiamo il bithift di sinistra, il prossimo, il bithift di destra, sarà facile. Tutto scorre a destra dell'importo specificato. L'unica piccola differenza è ciò che i bit vuoti vengono riempiti.
Se stiamo iniziando con un numero negativo (un numero binario dove il bit più a sinistra è un 1), tutti gli spazi vuoti sono riempiti con un 1. Se stiamo iniziando con un numero positivo (dove il bit più a sinistra, o il più significativo bit, è uno 0), quindi tutti gli spazi vuoti vengono riempiti con uno 0. Di nuovo, tutto torna al complemento a due.
Mentre questo sembra complicato, in pratica mantiene solo il segno del numero con cui iniziamo. Così -8 >> 2 == -2
mentre 8 >> 2 == 2
. Consiglierei di provare quelli fuori di te.
Poiché >> è l'opposto di <<, it's not surprising that shifting a number to the right is the same as dividing it by 2 to the power of shiftAmount. You may have noticed this from the example above. Again, if you can use this to avoid calling Math.pow
, otterrai un significativo incremento delle prestazioni.
Il nostro ultimo operatore bit a bit è lo spostamento a destra bit a bit senza segno. Questo è molto simile al normale spostamento a destra bit a bit, tranne per il fatto che tutti i bit vuoti a sinistra sono pieni di 0 secondi. Ciò significa che il risultato di questo operatore è sempre un numero intero positivo e tratta sempre il numero intero spostato come un numero intero senza segno. Non presenteremo un esempio di questo in questa sezione, ma ne vedremo presto l'uso.
Uno degli usi più pratici degli operatori bit a bit in Actionscript 3 sta funzionando con i colori, che vengono generalmente memorizzati come uints
.
Il formato standard per i colori è di scriverli in formato esadecimale: 0xAARRGGBB - ogni lettera rappresenta una cifra esadecimale. Qui, le prime due cifre esadecimali, che sono equivalenti alle prime otto cifre binarie, rappresentano il nostro alfa o trasparenza. Gli otto bit successivi rappresentano la quantità di rosso nel nostro colore (quindi un numero intero compreso tra 0 e 255), gli otto successivi la quantità di verde e gli otto finali rappresentano la quantità di blu nel nostro colore.
Senza operatori bit a bit, è estremamente difficile lavorare con i colori in questo formato, ma con loro è facile!
Sfida 1: trova la quantità di blu in un colore: Usando l'operatore &, prova a trovare la quantità di blu in un colore arbitrario.
funzione pubblica findBlueComponent (color: uint): uint // Il tuo codice qui!
Abbiamo bisogno di un modo per 'cancellare' o mascherare tutti gli altri dati in colore
e resta solo il componente blu. Questo è facile, in realtà! Se prendiamo colore e 0x000000FF
- o, scritto più semplicemente, colore e 0xFF
- finiamo con solo il componente blu.
Come puoi vedere da sopra e hai imparato nella descrizione dell'operatore &, qualsiasi cifra binaria & 0 sarà sempre uguale a 0, mentre qualsiasi cifra binaria e 1 manterrà il suo valore. Quindi se mascheriamo il nostro colore con 0xFF che ha solo 1s dove si trova il componente blu del nostro colore, finiamo con il solo componente blu.
Sfida 2: trova la quantità di rosso in un colore: Utilizzando due operatori bit per bit, prova a trovare la quantità di rosso in un colore arbitrario.
funzione pubblica findRedComponent (color: uint): uint // Il tuo codice qui!
In realtà abbiamo due soluzioni a questo problema. Uno sarebbe return (color & 0xFF0000) >> 16;
e l'altro sarebbe return (colore >> 16) & 0xFF;
Questo è molto simile alla Sfida 1, tranne che dobbiamo cambiare la nostra risposta ad un certo punto.
Sfida 3: trova la trasparenza di un colore: Utilizzando solo un operatore bit a bit, provare a trovare l'alfa di un colore (un numero intero compreso tra 0 e 255).
funzione pubblica findAlphaComponent (color: uint): uint // Il tuo codice qui!
Questo è un po 'più complicato. Dobbiamo stare attenti con quale operatore operante a turno giusto scegliamo. Perché stiamo lavorando con le cifre più a sinistra di a uint
, vogliamo usare l'operatore >>>. Quindi, la nostra risposta è semplicemente restituisce colore >>> 24;
.
Sfida finale: crea un colore dai suoi componenti: Usando il << and | operators, take the components of a color and merge them in to one uint
.
funzione pubblica createColor (a: uint, r: uint, g: uint, b: uint): uint // Il tuo codice qui!
Qui, dobbiamo spostare ciascun componente nella sua posizione corretta e quindi unirli. Vogliamo che Flash lo consideri come un numero intero senza segno, quindi lo abbiamo lanciato su a uint
: return uint ((a << 24) | (r << 16) | (g << 8) | b);
Avrai notato che ho trascurato di spiegare gli operatori bit a bit composti. Immagina di avere un intero x. Poi, x = x & 0xFF
equivale a x & = 0xFF
, x = x | 256
equivale a x | = 256
, e così via per il resto degli operatori composti.
Grazie per aver letto questo articolo! Spero che ora comprendiate gli operatori bit a bit e li possiamo utilizzare nel vostro codice AS3 (o in molte altre lingue!). Come sempre, se avete domande o commenti, per favore lasciateli qui sotto.