Forstå Bitwise Operators

Bitwise operatører er de merkelige ser operatørene som kan se vanskelig å forstå ... men ikke mer! Denne enkle å følge artikkelen vil hjelpe deg å forstå hva de er og hvordan du bruker dem, med et par praktiske eksempler så vel som å vise deg når og hvorfor du trenger dem.


Introduksjon

Bitwise operatører er operatører (akkurat som +, *, &&, etc.) som opererer på ints og uints på binærnivå. Dette betyr at de ser direkte på binære tall eller biter av et heltall. Alt dette virker skummelt, men i sannhet er bitvis operatører ganske enkle å bruke og også ganske nyttige!

Det er imidlertid viktig at du har en forståelse av binære tall og heksadesimale tall. Hvis du ikke gjør det, vennligst sjekk ut denne artikkelen - det vil virkelig hjelpe deg! Nedenfor er en liten applikasjon som lar deg prøve de forskjellige bitwise operatørene.

Ikke bekymre deg hvis du ikke forstår hva som skjer ennå, det vil alle være klart snart ...


Gjenkjenne bitwise operatørene

La oss ta en titt på bitwise operatørene som AS3 forsyner. Mange andre språk er ganske like (for eksempel JavaScript og Java har praktisk talt like operatører):

  • & (bitwise AND)
  • | (bitvis OR)
  • ~ (bitvis ikke)
  • ^ (bitwise XOR)
  • << (bitwise left shift)
  • >> (bitvis høyre skift)
  • >>> (bitvis usignert høyre skifte)
  • & = (bitwise og oppgave)
  • | = (bitwise OR assignment)
  • ^ = (bitvis XOR-oppgave)
  • <<= (bitwise left shift and assignment)
  • >> = (bitvis høyre skift og tildeling)
  • >>> = (bitvis usignert høyre skift og oppgave)

Det er et par ting du bør ta fra dette: Først ser noen bitwise operatører ut som operatører du har brukt før (& vs &&, | vs. ||). Dette er fordi de er noe lignende.

For det andre kommer de fleste bitwise operatører med a sammensatt oppgaveform av seg selv. Dette er det samme som hvordan du kan bruke + og + =, - og - =, osv.


Operatøren

Opp først: Bitwise OG-operatøren, &. En rask heads-up skjønt: normalt, ints og uints ta opp 4 byte eller 32 biter av plass. Dette betyr hver int eller UINT lagres som 32 binære sifre. For denne tutorials skyld, vil vi late som om det ints og uints bare ta opp 1 byte og har bare 8 binære sifre.

Operatøren sammenligner hvert binært tall med to heltall og returnerer et nytt heltall, med en 1 hvor begge tallene hadde en 1 og en 0 noe annet sted. Et diagram er verdt tusen ord, så her er en for å rydde opp ting. Det representerer å gjøre 37 og 23, som tilsvarer 5.

Legg merke til hvordan hvert binær siffer på 37 og 23 er sammenlignet, og resultatet har en 1 hvor både 37 og 23 hadde en 1, og resultatet har en 0 ellers.

En vanlig måte å tenke på binære sifre er som ekte eller falsk. Det vil si at 1 er ekvivalent med ekte og 0 er ekvivalent med falsk. Dette gjør at operatøren er mer fornuftig.

Når vi sammenligner to boolesker, gjør vi det normalt boolean1 && boolean2. Det uttrykket er bare sant hvis begge deler boolean1 og boolean2 er sanne. På samme måten, heltall 1 og heltall2 er ekvivalent, da & operatøren bare utgiver en 1 når begge binære sifrene i våre to heltall er 1.

Her er et bord som representerer den ideen:

En fin liten bruk av & operatøren er å kontrollere om et tall er jevnt eller merkelig. For heltall kan vi bare sjekke høyrebiten (også kalt minst signifikante biten) for å finne ut om heltallet er merkelig eller jevnt. Dette skyldes at når den konverteres til base 10, representerer den høyeste bit 20 eller 1. Når høyre side er 1, vet vi at tallet vårt er merkelig siden vi legger til 1 til en mengde krefter på to som alltid vil være like. Når høyre side er 0, vet vi at tallet vårt vil være like, siden det bare består av å legge opp en haug med like tall.

Her er et eksempel:

 var randInt: int = int (Math.random () * 1000); hvis (randInt & 1) trace ("oddetall.");  else trace ("Even nummer."); 

På min datamaskin var denne metoden ca 66% raskere enn bruk randInt% 2 for å sjekke om jevn og odde tall. Det er en ganske god ytelse!


The | Operatør

Opp neste er bitwise OR-operatøren, |. Som du kanskje har gjettet, er | operatøren er til || operatør som & operatør er til && operatøren. The | operatør sammenligner hvert binært tall på tvers av to heltall og gir tilbake en 1 hvis enten av dem er 1. Igjen, dette ligner på || operatør med booleans.

La oss ta en titt på det samme eksempelet som før, bortsett fra nå å bruke | operatør i stedet for & operatøren. Vi gjør nå 37 | 23 som tilsvarer 55:


Flagg: En Bruk av & og | operatører

Vi kan dra nytte av & and | operatører å tillate oss å overføre flere alternativer til en funksjon i en enkelt int.

La oss ta en titt på en mulig situasjon. Vi bygger en popup-vinduesklasse. På bunnen av det kan vi ha en Ja, Nei, Ok eller Avbryt-knapp eller hvilken som helst kombinasjon av dem - hvordan skal vi gjøre dette? Her er den harde måten:

 offentlig klasse PopupWindow utvider Sprite // Variabler, Constructor, etc ... offentlig statisk ugyldig showPopup (yesButton: Boolean, noButton: Boolean, okayButton: Boolean, cancelButton: Boolean) if (yesButton) // legg til JA knappen hvis ) // legg til NO-knapp // og så videre for resten av knappene

Er dette fryktelig? Nei. Men det er dårlig, hvis du er programmerer, å måtte slå opp rekkefølgen av argumenter hver gang du ringer til funksjonen. Det er også irriterende - for eksempel, hvis du bare vil vise Avbryt-knappen, må du sette alle de andre booleans til falsk.

La oss bruke det vi lærte om & og | å gjøre en bedre løsning:

 offentlig klasse PopupWindow strekker seg Sprite public static const YES: int = 1; offentlig statisk const NO: int = 2; offentlig statisk const OK: int = 4; offentlig statisk const CANCEL: int = 8; offentlig statisk tomt showPopup (knapper: int) hvis (knapper og JA) // legg til JA-knapp hvis (knapper og NEI) // legg til NO-knapp

Hvordan vil en programmør ringe til funksjonen, så Ja-knappen, Nei-knappen og Avbryt-knappen vises? Som dette:

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

Hva skjer? Det er viktig å merke seg at våre konstanter i det andre eksempelet er alle krefter av to. Så, hvis vi ser på deres binære skjemaer, vil vi legge merke til at de alle har et siffer som er lik 1, og resten er 0. De har faktisk et annet siffer lik 1. Dette betyr at uansett hvordan vi kombinerer dem med |, vil hver kombinasjon gi oss et unikt nummer. Ser på det på en annen måte, ut resultat av vår | erklæringen vil være et binært nummer med en 1 hvor alternativene våre hadde en 1.

For vårt nåværende eksempel har vi PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL som tilsvarer 1 | 2 | 8 som omskrives i binær er 00000001 | 00000010 | 00001000 som gir oss et resultat av 00001011.

Nå, i vår showPopup () funksjon, bruker vi & for å sjekke hvilke alternativer som ble sendt inn. For eksempel når vi sjekker knapper og YES, alle biter i JA er lik 0 unntatt den aller høyeste. Så, vi kontrollerer i hovedsak om den høyeste bit i knapper er en 1 eller ikke. Hvis det er, knapper og YES vil ikke være 0 og noe i if-setningen vil bli utført. Omvendt, hvis den høyeste punkt i knappene er 0, knapper og YES vil være 0, og if-setningen vil ikke bli utført.


Den ~ Operatør

Bitwise NOT-operatøren er litt annerledes enn de to vi har sett på så langt. I stedet for å ta et heltall på hver side av det, tar det et helt tall bare etter det. Dette er akkurat som! operatør, og ikke overraskende, det gjør en lignende ting. Faktisk, akkurat som! svinger en boolsk fra ekte til falsk eller omvendt, reverserer operatøren hvert binært tall i et heltall: fra 0 til 1 og 1 til 0:

Et raskt eksempel. Si at vi har hele tallet 37 eller 00100101. ~ 37 er da 11011010. Hva er basisverdien av dette? Vi vil…


To komplement, UINT vs. int, og mer!

Nå begynner moroa! Vi skal se nærmere på binære tall på en datamaskin. La oss begynne med UINT. Som nevnt tidligere, a UINT er vanligvis 4 byte eller 32 bit lang, noe som betyr at den har 32 binære sifre. Dette er lett å forstå: for å få basen 10 verdi konverterer vi bare nummeret til base 10 regelmessig. Vi får alltid et positivt nummer.

Men hva med int? Den bruker også 32 biter, men hvordan lagrer det negative tall? Hvis du gjettet at det første sifferet brukes til å lagre skiltet, er du på riktig vei. La oss ta en titt på to komplement system for lagring av binære tall. Mens vi ikke vil gå inn i alle detaljene her, brukes et to komplementsystem fordi det gjør binær aritmetikk enkelt.

For å finne de to komplementene til et binært tall, flipper vi ganske enkelt alle bitene (dvs. gjør hva operatøren gjør) og legg til en til resultatet. La oss prøve dette ut en gang:

Vi definerer deretter vårt resultat som verdien -37. Hvorfor gjør denne kompliserte prosessen og ikke bare vri den aller første biten og ring det -37?

Vel, la oss ta et enkelt uttrykk 37 + -37. Vi vet alle at dette burde være 0, og når vi legger til 37 til sine to komplement, er det det vi får:

Legg merke til at siden våre heltall bare inneholder åtte binære sifre, faller 1 i vårt resultat, og vi ender opp med 0, som vi burde.

For å oppsøke, for å finne det negative av et nummer, tar vi bare to komplement. Vi kan gjøre dette ved å invertere alle biter og legge til en.

Vil du prøve dette selv? Legg til spore (~ 37 + 1); til en AS3-fil, så kompilere og kjøre den. Du vil se -37 er skrevet ut, som det burde være.

Det er også en liten snarvei for å gjøre dette for hånd: Start fra høyre, arbeid til venstre til du kommer til en 1. Vri alle biter til venstre for denne første 1.

Når vi ser på et signert binært nummer (med andre ord, en som kan være negativ, en int ikke en UINT), kan vi se på venstre siffer for å fortelle om det er negativt eller positivt. Hvis det er en 0, er tallet positivt og vi kan konvertere til base 10 bare ved å beregne basen 10-verdien. Hvis venstrebiten er en 1, er tallet negativt, så vi tar to komplement av tallet for å få sin positive verdi, og deretter legger du ganske enkelt til et negativt tegn.

For eksempel, hvis vi har 11110010, vet vi at det er et negativt tall. Vi kan finne det to komplementet ved å bla alle sifrene til venstre for høyre 1, noe som gir oss 00001110. Dette er 13, så vi vet 11110010 tilsvarer -13.


Operatøren

Vi er tilbake til bitwise operatørene, og opp neste er bitwise XOR operatøren. Det er ingen tilsvarende boolesk operatør til denne.

Operatøren ^ ligner på & og | operatører ved at det tar en int eller UINT på begge sider. Når det beregnes det resulterende tallet, sammenligner det igjen de binære tallene i disse tallene. Hvis den ene eller den andre er en 1, vil den sette inn en 1 inn i resultatet, ellers vil den sette inn en 0. Dette er hvor navnet XOR, eller "eksklusivt eller" kommer fra.

La oss ta en titt på vårt vanlige eksempel:

Operatøren har bruk - det er spesielt bra for å bytte binære sifre - men vi vil ikke dekke noen praktiske applikasjoner i denne artikkelen.


De << Operator

Vi er nå på bitshift operatørene, spesielt den bitvise venstre shift operatøren her.

Disse jobber litt annerledes enn tidligere. I stedet for å sammenligne to heltall som &, |, og ^ gjorde disse operatørene et heltall. På venstre side av operatøren er heltallet som blir skiftet, og til høyre er hvor mye som skal skiftes av. Så for eksempel 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.

La oss ta en titt på dette eksemplet (husk, vi skal bare late som heltall har bare 8 biter i stedet for 32). Her har vi nummer 37 som sitter på sin fine blokk med minne 8 bits bred.

Ok, la oss skyve alle siffer over til venstre med 3, som 37 << 3 ville gjort:

Men nå har vi et lite problem - hva gjør vi med de 3 åpne bitene av minne der vi flyttet sifrene fra?

Selvfølgelig! Eventuelle tomme flekker erstattes bare med 0s. Vi ender opp med 00101000. Og det er alt der er til venstre bithift. Husk at Flash alltid synes resultatet av en venstre bithift er en int, ikke en UINT. Så hvis du trenger en UINT av en eller annen grunn må du kaste den til en UINT som dette: UINT (37 << 3). Denne støpingen endrer ikke faktisk noen av binær informasjon, bare hvordan Flash tolker det (hele to komplementet).

En interessant funksjon av venstre bithift er at det er det samme som å multiplisere et tall med to til shiftAmount-th-effekten. Så, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8. Hvis du kan bruke venstre skifte i stedet for Math.pow, du ser en enorm ytelsesøkning.

Du har kanskje lagt merke til at binærnummeret vi endte med, ikke var lik 37 * 8. Dette er bare fra vår bruk av bare 8 bits minne for heltall; Hvis du prøver det i ActionScript, får du det riktige resultatet. Eller prøv det med demoen øverst på siden!


>> Operatør

Nå som vi forstår venstre bithift, vil den neste, den rette bithift, være enkel. Alt lyser til høyre det beløpet vi spesifiserer. Den eneste lille forskjellen er hva de tomme biter blir fylt med.

Hvis vi starter med et negativt tall (et binært tall hvor venstre side er en 1), fylles alle tomme mellomrom med en 1. Hvis vi starter med et positivt tall (hvor den venstre bit eller mest signifikante bit, er en 0), så er alle tomme mellomrom fylt med en 0. Igjen går alt tilbake til to komplement.

Mens dette høres komplisert, opprettholder det i utgangspunktet bare tegnet på nummeret vi starter med. Så -8 >> 2 == -2 samtidig som 8 >> 2 == 2. Jeg vil anbefale å prøve dem ut på papir selv.

Siden >> er motsatt av <<, 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, du får en betydelig ytelse boost.


>>> Operatør

Vår endelige bitwise operatør er bitvis usignert høyre skift. Dette ligner veldig på den vanlige bitvise høyre skiftet, bortsett fra at alle tomme biter til venstre er fylt med 0s. Dette betyr at resultatet av denne operatøren alltid er et positivt heltall, og det behandler alltid heltallet som skiftes som et usignert heltall. Vi vil ikke løpe gjennom et eksempel på dette i denne delen, men vi vil se en bruk for det veldig kort tid.


Bruke bitwise operatører å jobbe med farger

En av de mest praktiske bruken av bitwise operatører i Actionscript 3 arbeider med farger, som vanligvis lagres som uints.

Standardformatet for farger er å skrive dem i heksadesimale: 0xAARRGGBB - hvert bokstav representerer et heksadesimale siffer. Her representerer de to første heksadesimale sifrene, som tilsvarer de første åtte binære sifrene, vår alfa eller gjennomsiktighet. De neste åtte bitene representerer mengden rød i vår farge (så et heltall fra 0 til 255), de neste åtte mengden grønt, og den endelige åtte representerer mengden blå i fargen vår.

Uten bitwise operatører er det ekstremt vanskelig å jobbe med farger i dette formatet - men med dem er det enkelt!

Utfordring 1: Finn mengden blå i en farge: Bruk & operatøren, prøv å finne mengden blå i en vilkårlig farge.

 offentlig funksjon findBlueComponent (farge: uint): uint // Din kode her! 

Vi trenger en måte å "slette" eller maskere alle de andre dataene i farge og bare ha den blå komponenten igjen. Dette er enkelt, faktisk! Hvis vi tar farge og 0x000000FF - eller, skrevet mer enkelt, farge og 0xFF - vi ender opp med bare den blå komponenten.

Som du kan se ovenfra og du lærte i beskrivelsen av & operatøren, vil alle binære siffer og 0 alltid være 0, mens et hvilket som helst binært tall og 1 vil beholde verdien. Så hvis vi maskerer fargen vår med 0xFF som bare har 1s der den blå komponenten av fargen vår er, slutter vi med bare den blå komponenten.

Utfordring 2: Finn mengden rød i en farge: Bruk to bitwise operatorer, prøv å finne mengden rød i en vilkårlig farge.

 offentlig funksjon findRedComponent (farge: uint): uint // Din kode her! 

Vi har faktisk to løsninger på dette problemet. En ville være returnere (farge og 0xFF0000) >> 16; og den andre ville være returnere (farge >> 16) & 0xFF;

Dette ligner svært utfordring 1, bortsett fra at vi må skifte svaret vårt på et tidspunkt.

Utfordring 3: Finn gjennomsiktigheten av en farge: Bruk bare en bitvis operatør, prøv å finne alfa av en farge (et heltall fra 0 til 255).

 offentlig funksjon findAlphaComponent (farge: uint): uint // Din kode her! 

Denne er en touch vanskeligere. Vi må være forsiktige med hvilken høyre skiftoperatør vi velger. Fordi vi jobber med venstre siffer i a UINT, Vi vil bruke >>> operatør. Så, vårt svar er rett og slett returfarge >>> 24;.

Endelig utfordring: Lag en farge fra komponentene: Bruker << and | operators, take the components of a color and merge them in to one UINT.

 offentlig funksjon createColor (a: uint, r: uint, g: uint, b: uint): uint // Din kode her! 

Her må vi skifte hver komponent til riktig posisjon, og deretter fusjonere dem. Vi vil at Flash skal behandle det som et usignert heltall, så vi kaster det til en UINT: returner uint ((a << 24) | (r << 16) | (g << 8) | b);


Sammensatt operatører

Du har kanskje lagt merke til at jeg har forsømt å forklare de sammensatte bitwise operatørene. Tenk deg at vi har et heltall x. Deretter, x = x & 0xFF er det samme som x & = 0xFF, x = x | 256 er det samme som x | = 256, og så videre for resten av sammensatte operatører.


Konklusjon

Takk for at du leser denne artikkelen! Jeg håper du forstår bitwise operatører og kan bruke dem i AS3-koden din (eller på mange andre språk!). Som alltid, hvis du har noen spørsmål eller kommentarer, vennligst legg dem under.