Înțelegerea operatorilor de biți

Operatorii biți sunt acei operatori ciudați care ar putea să pară greu de înțeles ... dar nu mai mult! Acest articol ușor de urmărit vă va ajuta să înțelegeți ce sunt și cum să le utilizați, cu câteva exemple practice, pentru a vă arăta când și de ce aveați nevoie de ele.


Introducere

Operatorii biți sunt operatori (la fel ca +, *, &&, etc.) care operează pe Ints și uints la nivelul binar. Aceasta înseamnă că se uită direct la cifrele binare sau biții unui întreg. Toate acestea sună înfricoșător, dar, în realitate, operatorii biți sunt destul de ușor de folosit și, de asemenea, destul de folositori!

Este important, totuși, că aveți o înțelegere a numerelor binare și a numerelor hexazecimale. Dacă nu, vă rugăm să verificați acest articol - vă va ajuta cu adevărat! Mai jos este o mică aplicație care vă va permite să încercați diferiți operatori de biți.

Nu vă faceți griji dacă nu înțelegeți ce se întâmplă încă, va fi clar în curând ...


Recunoașterea operatorilor de biți

Să aruncăm o privire la operatorii bituminoși pe care AS3 le furnizează. Multe alte limbi sunt destul de similare (de exemplu, JavaScript și Java au practic operatori identici):

  • & (bitwise AND)
  • | (bit OR)
  • ~ (NU este bit)
  • ^ (XOR pe biți)
  • << (bitwise left shift)
  • >> (schimbare dreapta biți)
  • >>> (schimbare dreapta nesemnată)
  • & = (atribuire biți ȘI)
  • | = (atribuire biți OR)
  • ^ = (alocarea XOR pe biți)
  • <<= (bitwise left shift and assignment)
  • >> = (schimbare biți și asignare biți)
  • >>> = (deplasare și asignare în dreptul biți nesemnate)

Există câteva lucruri pe care ar trebui să le luați din acest motiv: În primul rând, unii operatori de biți arată asemănători cu operatorii pe care i-ați folosit înainte (& vs &&, | vs. ||). Asta pentru că ei sunteți oarecum similară.

În al doilea rând, operatorii cu cele mai multe biți vin cu a formula de asignare complexă de la sine. Acesta este același lucru cu modul în care puteți folosi + și + =, - și - =, etc.


Operatorul

Sus în primul rând: operatorul AND și bit. Cu toate acestea, un heads-up rapid: în mod normal, Ints și uints ocupa 4 octeți sau 32 de biți de spațiu. Asta înseamnă fiecare int sau uint este stocată ca 32 de cifre binare. De dragul acestui tutorial, ne vom preface uneori Ints și uints doar preluați un octet și aveți doar 8 cifre binare.

Operatorul & compară fiecare cifră binară a două numere întregi și returnează un întreg întreg, cu un număr de 1 oriunde ambele numere au un 1 și un 0 oriunde altundeva. O diagramă este în valoare de o mie de cuvinte, așa că trebuie să clarificăm lucrurile. Ea reprezintă acțiunea 37 și 23, care este egal 5.

Observați modul în care se compară fiecare cifră binară de 37 și 23, iar rezultatul are o valoare 1 dacă ambele 37 și 23 au o valoare 1 și rezultatul are 0 altfel.

Un mod comun de a gândi despre cifrele binare este la fel Adevărat sau fals. Asta este, 1 este echivalent cu Adevărat și 0 este echivalent cu fals. Acest lucru face ca operatorul și operatorul să aibă mai multă sens.

Când comparăm două booleani, facem în mod normal boolean1 && boolean2. Această expresie este adevărată numai dacă ambele boolean1 și boolean2 sunt adevărate. In acelasi fel, integer1 & integer2 este echivalentă, deoarece operatorul & afișează numai 1 atunci când ambele cifre binare ale celor două numere întregi sunt 1.

Iată un tabel care reprezintă ideea:

O utilizare minuțioasă a operatorului & este de a verifica dacă un număr este chiar sau ciudat. Pentru numere întregi, putem verifica pur și simplu bitul din dreapta (numit și bitul cel mai puțin semnificativ) pentru a determina dacă întregul este impar sau par. Acest lucru se datorează faptului că atunci când se convertește la baza 10, bitul din dreapta reprezintă 20 sau 1. Când bitul din dreapta este 1, știm că numărul nostru este ciudat, deoarece adăugăm 1 la o grămadă de puteri de două care va fi întotdeauna egal. Când bitul din dreapta este 0, știm că numărul nostru va fi egal, deoarece constă pur și simplu în adăugarea unui număr de numere par.

Iată un exemplu:

 var randInt: int = int (Math.random () * 1000); dacă (randInt & 1) trace ("Număr impar.");  altceva trace ("Even number".); 

Pe calculatorul meu, această metodă a fost cu aproximativ 66% mai rapidă decât utilizarea randInt% 2 pentru a verifica numerele par și impare. Acesta este un impresionant performanță!


| | Operator

Următorul este operatorul de biți OR, |. Așa cum probabil ați ghicit, | operatorul este la || operatorul și operatorul este pentru operatorul &&. | | operatorul compară fiecare cifră binară între două numere întregi și dă înapoi un 1 dacă fie dintre acestea sunt 1. Din nou, aceasta este similară cu || operator cu booleani.

Să aruncăm o privire la același exemplu ca înainte, exceptând acum utilizarea lui | operator în locul operatorului. Acum ne descurcăm 37 | 23 care este egal cu 55:


Steaguri: A Utilizarea & și | operatorii

Putem profita de & și | operatori pentru a ne permite să trecem mai multe opțiuni la o funcție într-un singur int.

Să aruncăm o privire la o situație posibilă. Construim o clasă de ferestre pop-up. În partea de jos a acestuia, putem avea un buton Da, Nu, Bine, sau Anulare sau orice combinație a acestora - cum ar trebui să facem asta? Iată modul greu:

 clasa publica PopupWindow extinde Sprite // Variables, Constructor, etc ... public static void showPopup (daButton: Boolean, noButton: Boolean, okButton: Boolean, cancelButton: Boolean) if (yesButton) // add YES button ) // adăugați butonul NO și așa mai departe pentru celelalte butoane

E oribil acest lucru? Nu. Dar este rău, dacă sunteți un programator, să trebuiască să căutați ordinea argumentelor de fiecare dată când apelați funcția. De asemenea, este enervant - de exemplu, dacă doriți doar să afișați butonul Anulare, trebuie să setați toate celelalte booleane la fals.

Să folosim ceea ce am învățat despre & și | pentru a face o soluție mai bună:

 clasa publică PopupWindow extinde Sprite public static const YES: int = 1; statică statică publică NO: int = 2; statică statică publică OKAY: int = 4; statică publică const CANCEL: int = 8; (butoane și & YES) // adăugați butonul YES dacă (butoane și nr) // adăugați butonul NO

Cum ar putea un programator să apeleze funcția astfel încât să apară butonul Da, butonul Nu și butonul Anulare? Asa:

 PopupWindow.show (PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL);

Ce se întâmplă? Este important de menționat că constantele noastre din al doilea exemplu sunt toate puterile a două. Deci, dacă ne uităm la formele binare, vom observa că toți au o cifră egală cu 1, iar restul egal cu 0. De fapt, fiecare are o cifră diferită egală cu 1. Aceasta înseamnă că indiferent de modul în care combinăm cu fiecare combinație ne va da un număr unic. Privind la ea într-un mod diferit, rezultatul nostru | instrucțiunea va fi un număr binar cu un număr de 1 în cazul în care opțiunile noastre au avut 1.

Pentru exemplul nostru curent, avem PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL care este echivalentă cu 1 | 2 | 8 care este rescris în binar este 00000001 | 00000010 | 00001000 care ne dă un rezultat din 00001011.

Acum, în nostru Afișați popup() funcția, folosim & pentru a verifica ce opțiuni au fost transmise. De exemplu, când verificăm butoanele & YES, toți biții din DA sunt egali cu 0, cu excepția celui cel mai din dreapta. Deci, verificăm în esență dacă bitul din dreapta din butoane este 1 sau nu. Dacă este, butoanele & YES nu va fi egal cu 0 și nimic din instrucțiunea if va fi executat. În schimb, dacă bitul din dreapta este butonul 0, butoanele & YES va fi egal cu 0, iar instrucțiunea if nu va fi executat.


Operatorul

Operatorul de biți NOT este puțin diferit de cele două pe care le-am analizat până acum. În loc de a lua un număr întreg pe fiecare parte a acesteia, este nevoie de un număr întreg numai după ea. Acest lucru este la fel ca și! operator, și, fără îndoială, face un lucru similar. De fapt, așa cum! aruncă un boolean de la Adevărat la fals sau invers, operatorul ~ inversează fiecare cifră binară într-un număr întreg: de la 0 la 1 și de la 1 la 0:

Un exemplu rapid. Spunem că avem numărul întreg 37, sau 00100101. ~ 37 este 11011010. Care este valoarea de baza 10? Bine…


Complementul a două, uint vs. int, și altele!

Acum începe distracția! Vom analiza mai îndeaproape numerele binare pe un computer. Să începem cu uint. Așa cum am menționat anterior, a uint este de obicei de 4 octeți sau 32 de biți, ceea ce înseamnă că are 32 de cifre binare. Acest lucru este ușor de înțeles: pentru a obține valoarea de bază 10, pur și simplu transformăm numărul în baza 10 în mod regulat. Vom primi întotdeauna un număr pozitiv.

Dar ce zici de int? De asemenea, folosește 32 de biți, dar cum stochează numere negative? Dacă ați ghicit că prima cifră este utilizată pentru a stoca semnul, sunteți pe calea cea bună. Să aruncăm o privire la complementul celor doi sistem de stocare a numerelor binare. În timp ce nu vom intra în toate detaliile de aici, se folosește un sistem complementar de două, deoarece face aritmetica binară ușoară.

Pentru a găsi complementul celor doi cu un număr binar, pur și simplu răsturnați toți biții (adică fă ceea ce face operatorul ~) și adăugați unul la rezultat. Să încercăm o dată acest lucru:

Apoi definim rezultatul nostru ca valoare -37. De ce acest proces complicat și nu doar să răstoarneți primul bit și să-l sunați pe -37?

Să luăm o expresie simplă 37 ± 37. Știm cu toții că acest lucru ar trebui să fie egal cu 0, iar când adăugăm 37 la complementul celor doi, asta ajungem:

Observați că, deoarece numerele noastre întregi dețin doar opt cifre binare, rezultatul 1 din rezultatul nostru este abandonat și ajungem la 0, așa cum ar trebui.

Pentru a revedea, pentru a găsi negativul unui număr, luăm pur și simplu complementul celor doi. Putem face acest lucru inversând toți biții și adăugând unul.

Vrei să încerci singur asta? Adăuga trace (~ 37 + 1); la un fișier AS3, apoi compilați și rulați-l. Veți vedea că -37 este tipărit, așa cum ar trebui să fie.

Există, de asemenea, o mică comandă rapidă pentru a face acest lucru cu mâna: începând din dreapta, lucrați la stânga până când ajungeți la 1. Îndepărtați toți biții din stânga acestui prim 1.

Când ne uităm la un număr binar semnat (cu alte cuvinte, unul care poate fi negativ, un int nu a uint), ne putem uita la cifra din stânga pentru a spune dacă este negativă sau pozitivă. Dacă este un 0, atunci numărul este pozitiv și putem converti la baza 10 pur și simplu prin calcularea valorii sale de bază 10. Dacă bitul din stânga este 1, atunci numărul este negativ, deci luăm complementul celor doi pentru a obține valoarea sa pozitivă și apoi adăugăm pur și simplu un semn negativ.

De exemplu, dacă avem 11110010, știm că este un număr negativ. Putem găsi complementul celor doi prin rotirea tuturor cifrelor la stânga celui din dreapta 1, oferindu-ne 00001110. Aceasta este egal cu 13, deci știm că 11110010 este egal cu -13.


Operatorul

Ne întoarcem la operatorii bitum, iar următorul este operatorul XOR cu biți. Nu există un operator boolean echivalent cu acesta.

Operatorul ^ este similar cu & și | operatorii în că este nevoie de o int sau uint de ambele părți. Când se calculează numărul rezultat, acesta compară din nou cifrele binare ale acestor numere. Dacă unul sau celălalt este un 1, acesta va introduce un rezultat de 1 ină, altfel va introduce un 0. Acesta este locul unde numele XOR sau "exclusiv sau" vine de la.

Să aruncăm o privire la exemplul nostru obișnuit:

Operatorul ^ are utilizări - este deosebit de bun pentru comutarea cifrelor binare - dar nu vom acoperi toate aplicațiile practice din acest articol.


<< Operator

Suntem acum pe operatorii de biti, adică pe operatorul de deplasare cu biți din stânga aici.

Acestea funcționează puțin diferit decât înainte. În loc să comparăm două numere întregi ca &, |, și ^, acești operatori schimbă un număr întreg. În partea stângă a operatorului se află întregul care este deplasat, iar în partea dreaptă este cantitatea de trecere. Deci, de exemplu, 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.

Să aruncăm o privire la acest exemplu (amintiți-vă, doar vom pretinde că întregii au doar 8 biți în loc de 32). Aici avem numărul 37 așezat pe blocul frumos de memorie de 8 biți lățime.

Bine, să alunecăm toate cifrele de la stânga cu 3, ca 37 << 3 ar face:

Dar acum avem o mică problemă - ce facem cu cele trei bucăți deschise de memorie de unde am mutat cifrele?

Desigur! Orice pete goale sunt inlocuite cu 0s. Încheiem cu 00101000. Și asta e tot la bitshiftul stâng. Rețineți că Flash se gândește întotdeauna că rezultatul unui bitshift stânga este unul int, nu a uint. Deci, dacă aveți nevoie de a uint pentru un motiv oarecare, va trebui să-l aruncați la a uint asa: uint (37 << 3). Această distribuție nu schimbă, de fapt, niciuna dintre informațiile binare, așa cum îl interpretează Flash (chestia completă a celor doi).

O caracteristică interesantă a bitshift-ului stânga este aceea că este același cu înmulțirea unui număr de doi cu puterea de schimbare a puterii. Asa de, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8. Dacă aveți posibilitatea să utilizați schimbarea stânga în loc de Math.pow, veți vedea o creștere imensă a performanței.

S-ar putea să fi observat că numărul binar cu care am ajuns nu a fost egal cu 37 * 8. Aceasta este doar din utilizarea noastră de numai 8 biți de memorie pentru numere întregi; dacă încercați în ActionScript, veți obține rezultatul corect. Sau, încercați-l cu demo-ul din partea de sus a paginii!


Operatorul >>

Acum că înțelegem bitshift-ul stâng, următorul, bitshift-ul corect, va fi ușor. Totul glisă spre dreapta suma specificată. Singura diferență ușoară este ceea ce biții goi se umple.

Dacă începem cu un număr negativ (un număr binar unde bitul din stânga este 1), toate spațiile goale sunt umplute cu 1. Dacă începem cu un număr pozitiv (în cazul în care bitul cel mai din stânga sau cel mai semnificativ bit, este un 0), atunci toate spațiile goale sunt umplute cu 0. Din nou, totul se întoarce la complementul celor doi.

În timp ce acest lucru sună complicat, în principiu doar păstrează semnul numărului de la care începem. Asa de -8 >> 2 == -2 in timp ce 8 >> 2 == 2. Aș recomanda să-i încercați singur pe hârtie.

Din moment ce >> este opusul <<, 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, veți obține un impuls semnificativ de performanță.


>>> Operatorul

Operatorul nostru bitwise final este trecerea de dreapta nesemnată. Acest lucru este foarte asemănător schimbării regulate bi-dreapta, cu excepția faptului că toți biți goi din stânga sunt umpluți cu 0s. Aceasta înseamnă că rezultatul acestui operator este întotdeauna un întreg pozitiv și întotdeauna tratează întregul care este mutat ca un întreg nesemnat. Nu vom trece printr-un exemplu de acest lucru în această secțiune, dar vom vedea o utilizare foarte curând.


Utilizarea operatorilor de biți pentru a lucra cu culori

Una dintre cele mai practice utilizări ale operatorilor de biți în Actionscript 3 lucrează cu culori, care sunt stocate în mod obișnuit ca uints.

Formatul standard pentru culori este să le scrieți în hexazecimal: 0xAARRGGBB - fiecare literă reprezintă o cifră hexazecimală. Aici, primele două cifre hexazecimale, care sunt echivalente cu primele opt cifre binare, reprezintă alfa-ul nostru sau transparența. Următorii opt biți reprezintă cantitatea de culoare roșie în culoarea noastră (deci un număr întreg de la 0 la 255), următorii opt cantități de verde și cei opt finali reprezintă cantitatea de albastru în culoarea noastră.

Fără operatori de biți, este extrem de dificil să lucrezi cu culori în acest format - dar cu ei este ușor!

Provocarea 1: găsiți cantitatea de albastru într-o culoare: Utilizând operatorul &, încercați să găsiți cantitatea de albastru într-o culoare arbitrară.

 funcția publică findBlueComponent (culoare: uint): uint // Codul tău aici! 

Avem nevoie de o modalitate de a "șterge" sau de a masca toate celelalte date din culoare și pur și simplu ai lăsat componenta albastră. Acest lucru este ușor, de fapt! Dacă luăm culoare & 0x000000FF - sau, scrise mai simplu, culoare & 0xFF - ajungem doar cu componenta albastră.

După cum puteți vedea mai sus și ați învățat în descrierea operatorului &, orice cifră binară & 0 va fi întotdeauna egală cu 0, în timp ce orice cifră binară & 1 își păstrează valoarea. Deci, dacă ne maschează culoarea cu 0xFF care are doar 1s în cazul în care componenta albastră a culorii noastre este localizată, ajungem cu doar componenta albastră.

Provocarea 2: găsiți cantitatea de roșu într-o culoare: Folosind doi operatori de biți, încercați să găsiți cantitatea de roșu într-o culoare arbitrară.

 funcția publică findRedComponent (culoare: uint): uint // Codul tău aici! 

Avem de fapt două soluții la această problemă. Unul ar fi retur (culoare & 0xFF0000) >> 16; și cealaltă ar fi retur (culoare >> 16) & 0xFF;

Acest lucru este foarte similar cu Provocarea 1, cu excepția faptului că trebuie să ne schimbăm răspunsul la un moment dat.

Provocarea 3: Găsiți transparența unei culori: Folosind un singur operator bitwise, încercați să găsiți alfa unei culori (un număr întreg de la 0 la 255).

 funcția publică findAlphaComponent (culoare: uint): uint // Codul tău aici! 

Acesta este un lucru mai complicat. Trebuie să fim atenți cu ce operatorul de schimbare a vitezei alegem. Pentru că lucrăm cu cifrele din stânga unui uint, dorim să folosim >>> operatorul. Deci, răspunsul nostru este pur și simplu culoarea revenirii >>> 24;.

Final Challenge: Creați o culoare din componentele sale: Utilizarea << and | operators, take the components of a color and merge them in to one uint.

 funcția publică createColor (a: uint, r: uint, g: uint, b: uint): uint // Codul tău aici! 

Aici, trebuie să schimbăm fiecare componentă în poziția sa corectă și apoi să o îmbinăm. Vrem ca Flash să o trateze ca pe un întreg nesemnificat, așa că l-am aruncat la a uint: retur uint ((a << 24) | (r << 16) | (g << 8) | b);


Operatori compuși

S-ar putea să fi observat că am neglijat explicarea operatorilor de biți compuși. Imaginați-vă că avem un număr întreg x. Atunci, x = x & 0xFF este la fel ca x & = 0xFF, x = x | 256 este la fel ca x | = 256, și așa mai departe pentru ceilalți operatori compuși.


Concluzie

Vă mulțumim că ați citit acest articol! Sper că acum înțelegeți operatorii de biți și le puteți utiliza în codul AS3 (sau în multe alte limbi!). Ca întotdeauna, dacă aveți întrebări sau comentarii, lăsați-le mai jos.

Cod