Bitwise-operators zijn die vreemd uitziende operatoren die er misschien moeilijk uitzien om te begrijpen ... maar niet meer! Dit gemakkelijk te volgen artikel zal u helpen te begrijpen wat ze zijn en hoe ze te gebruiken, met een paar praktische voorbeelden om u te laten zien wanneer en waarom u ze nodig hebt.
Bitwise-operators zijn operators (net als +, *, &&, etc.) die werken ints
en uints
op het binaire niveau. Dit betekent dat ze rechtstreeks naar de binaire cijfers of bits van een geheel getal kijken. Dit klinkt allemaal angstaanjagend, maar in werkelijkheid zijn bitsgewijze operatoren vrij gemakkelijk te gebruiken en ook behoorlijk bruikbaar!
Het is echter belangrijk dat u een goed begrip hebt van binaire getallen en hexadecimale getallen. Als je dit niet doet, bekijk dan dit artikel - het zal je echt helpen! Hieronder vindt u een kleine applicatie waarmee u de verschillende bitwise-operators kunt uitproberen.
Maak je geen zorgen als je niet begrijpt wat er aan de hand is, het zal allemaal snel duidelijk zijn ...
Laten we eens kijken naar de bitwise-operators die AS3 levert. Veel andere talen lijken erg op elkaar (bijvoorbeeld JavaScript en Java hebben vrijwel identieke operators):
Hier zijn een paar dingen die u moet doen: Ten eerste lijken sommige bitsgewijze operatoren op operators die u eerder hebt gebruikt (& vs. &&, | vs. ||). Dit komt omdat zij zijn enigszins vergelijkbaar.
Ten tweede, de meeste bitwise-operators worden geleverd met een samengestelde opdrachtformulier van hunzelf. Dit is hetzelfde als hoe u + en + =, - en - =, etc. kunt gebruiken.
Als eerste: de bitwise operator AND, &. Een snelle heads-up hoor: normaal, ints
en uints
neem 4 bytes of 32 bits ruimte in beslag. Dit betekent elk int
of uint
wordt opgeslagen als 32 binaire cijfers. In het belang van deze tutorial doen we dat soms ints
en uints
neem slechts 1 byte op en heeft slechts 8 binaire cijfers.
De operator & vergelijkt elk binair cijfer van twee gehele getallen en retourneert een nieuw geheel getal, met een 1 overal waar beide getallen een 1 en een 0 hadden. Een diagram zegt meer dan duizend woorden, dus hier is er eentje om dingen op te helderen. Het vertegenwoordigt doen 37 & 23
, wat gelijk is aan 5
.
Merk op hoe elk binair cijfer van 37 en 23 wordt vergeleken, en het resultaat heeft een 1 waar zowel 37 als 23 een 1 hadden, en het resultaat heeft een 0 anders.
Een veel voorkomende manier van denken over binaire cijfers is als waar
of vals
. Dat wil zeggen, 1 is gelijk aan waar
en 0 is gelijk aan vals
. Dit maakt de operator & meer zinvol.
Wanneer we twee booleans vergelijken, doen we dat normaal gesproken boolean1 && boolean2
. Die uitdrukking is alleen waar als beide boolean1
en boolean2
zijn waar. Op dezelfde manier, geheel getal1 & geheel getal2
is equivalent, omdat de operator & alleen een 1 uitvoert als beide binaire cijfers van onze twee gehele getallen 1 zijn.
Hier is een tabel die dat idee vertegenwoordigt:
Een netjes gebruik van de operator & om te controleren of een getal even of oneven is. Voor gehele getallen kunnen we eenvoudig het meest rechtse bit controleren (ook wel het minst significante bit genoemd) om te bepalen of het gehele getal oneven of even is. Dit komt omdat bij het converteren naar basis 10, het meest rechtse bit 2 vertegenwoordigt0 of 1. Als het meest rechtse bit 1 is, weten we dat ons getal vreemd is, omdat we er 1 aan een aantal machten van twee toevoegen, die altijd even zijn. Als het meest rechtse bit 0 is, weten we dat ons nummer even is, omdat het simpelweg bestaat uit het samenvoegen van een aantal even nummers.
Hier is een voorbeeld:
var randInt: int = int (Math.random () * 1000); if (randInt & 1) trace ("Odd number."); else trace ("Even nummer.");
Op mijn computer was deze methode ongeveer 66% sneller dan het gebruik randInt% 2
om te controleren op even en oneven getallen. Dat is een flinke prestatieverbetering!
De volgende is de bitsgewijze OR-operator, |. Zoals je misschien al geraden hebt, is de | operator is naar de || operator als de operator & naar de operator &&. De | operator vergelijkt elk binair cijfer over twee gehele getallen en geeft een 1 als terug een van beide van hen zijn 1. Nogmaals, dit is vergelijkbaar met de || operator met booleans.
Laten we hetzelfde voorbeeld als hiervoor bekijken, behalve nu met | | operator in plaats van de operator &. Dat zijn we nu aan het doen 37 | 23
wat gelijk staat aan 55:
We kunnen profiteren van de & en | operators om ons in staat te stellen meerdere opties door te geven aan een functie in een single int
.
Laten we een mogelijke situatie bekijken. We bouwen een pop-upvensterklasse. Aan de onderkant ervan kunnen we een Ja, Nee, Ok of Annuleer knop of een combinatie hiervan hebben - hoe moeten we dit doen? Dit is de moeilijke manier:
public class PopupWindow breidt Sprite uit // Variables, Constructor, etc ... public static void showPopup (yesButton: Boolean, noButton: Boolean, okayButton: Boolean, cancelButton: Boolean) if (yesButton) // add YES button if (noButton ) // NO-knop toevoegen // enzovoort voor de rest van de knoppen
Is dit vreselijk? Nee. Maar het is erg, als je een programmeur bent, om de volgorde van argumenten op te zoeken telkens als je de functie aanroept. Het is ook vervelend - als u bijvoorbeeld alleen de knop Annuleren wilt weergeven, moet u alle andere instellen Booleans
naar vals
.
Laten we gebruiken wat we hebben geleerd over & en | om een betere oplossing te maken:
public class PopupWindow breidt Sprite uit public static const YES: int = 1; openbare statische const NO: int = 2; public static const OKAY: int = 4; public static const CANCEL: int = 8; public static void showPopup (knoppen: int) if (buttons & YES) // add YES button if (buttons & NO) // add NO button
Hoe kan een programmeur de functie oproepen zodat de knop Ja, de knop Nee en de knop Annuleren worden weergegeven? Zoals dit:
PopupWindow.show (PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL);
Wat gebeurd er? Het is belangrijk op te merken dat onze constanten in het tweede voorbeeld alle machten van twee zijn. Dus als we naar hun binaire vormen kijken, zullen we merken dat ze allemaal één cijfer gelijk aan 1 hebben en de rest gelijk is aan 0. In feite hebben ze elk een ander cijfer gelijk aan 1. Dit betekent dat het niet uitmaakt hoe we combineren met |, elke combinatie geeft ons een uniek nummer. Als je er op een andere manier naar kijkt, is het resultaat van onze | statement is een binair getal met een 1, waar onze opties een 1 hadden.
Voor ons huidige voorbeeld hebben we PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL
wat gelijk is aan 1 | 2 | 8
die in binair herschreven is 00000001 | 00000010 | 00001000
wat ons een resultaat geeft 00001011
.
Nu, in onze toon popup()
functie gebruiken we & om te controleren welke opties zijn doorgegeven. Bijvoorbeeld wanneer we controleren knoppen en JA
, alle bits in YES zijn gelijk aan 0 behalve de meest rechtse. We controleren dus in feite of het meest rechtse bit in knoppen een 1 is of niet. Als het is, knoppen en JA
zal niet gelijk zijn aan 0 en alles in de if-statement zal worden uitgevoerd. Omgekeerd, als het meest rechtse bit in knoppen 0 is, knoppen en JA
is gelijk aan 0 en de if-instructie zal niet worden uitgevoerd.
De bitsgewijze operator NOT is iets anders dan de twee die we tot nu toe hebben bekeken. In plaats van een geheel getal aan elke zijde te nemen, is er pas een geheel getal nodig. Dit is net als het! operator, en, niet verrassend, het doet iets soortgelijks. Eigenlijk net zoals! gooit een booleaanse waarde om waar
naar vals
of omgekeerd, de operator ~ keert elk binair cijfer in een geheel getal om: van 0 tot 1 en van 1 tot 0:
Een snel voorbeeld. Stel dat we het gehele getal 37 of 00100101 hebben. ~ 37 is dan 11011010. Wat is de basis 10-waarde hiervan? Goed…
uint
vs. int
, en meer!Nu begint het plezier! We gaan binaire getallen op een computer nader bekijken. Laten we beginnen met de uint
. Zoals eerder vermeld, uint
is meestal 4 bytes of 32 bits lang, wat betekent dat het 32 binaire cijfers heeft. Dit is gemakkelijk te begrijpen: om de waarde van base 10 te krijgen, converteren we het nummer eenvoudig naar base 10. We krijgen altijd een positief cijfer.
Maar hoe zit het met de int
? Het gebruikt ook 32 bits, maar hoe slaat het negatieve getallen op? Als je raadt dat het eerste cijfer wordt gebruikt om het bord op te slaan, ben je op het goede pad. Laten we eens kijken naar de twee's complement systeem voor het opslaan van binaire getallen. Hoewel we hier niet alle details zullen bespreken, wordt een twee-complement-systeem gebruikt omdat het binaire rekenkunde eenvoudig maakt.
Om de twee's complement van een binair getal te vinden, draaien we eenvoudig alle bits om (d.w.z. doe wat de operator ~ doet) en voeg een toe aan het resultaat. Laten we dit eens proberen:
Vervolgens definiëren we ons resultaat als de waarde -37. Waarom dit gecompliceerde proces en niet alleen het allereerste bit omdraaien en dat -37 noemen?
Laten we een eenvoudige uitdrukking aannemen 37 + -37
. We weten allemaal dat dit gelijk moet zijn aan 0, en als we de 37 toevoegen aan het complement van zijn twee, dan krijgen we dat:
Merk op dat aangezien onze gehele getallen slechts acht binaire cijfers bevatten, de 1 in ons resultaat wordt weggelaten en we eindigen met 0, zoals we zouden moeten doen.
Om samen te vatten, om het negatief van een getal te vinden, nemen we eenvoudig het complement van de twee. We kunnen dit doen door alle bits om te keren en er een toe te voegen.
Wil je dit zelf proberen? Toevoegen trace (~ 37 + 1);
naar een AS3-bestand, compileer en voer het uit. U zult zien dat -37 wordt afgedrukt, zoals het zou moeten zijn.
Er is ook een kleine snelkoppeling om dit met de hand te doen: begin van rechts, werk naar links totdat je een 1 bereikt. Draai alle bits links van deze eerste 1 om.
Wanneer we naar een ondertekend binair nummer kijken (met andere woorden, eentje die negatief kan zijn, een int
geen a uint
), we kunnen naar het meest linkse cijfer kijken om te zien of het negatief of positief is. Als het een 0 is, is het getal positief en kunnen we eenvoudig naar basis 10 converteren door de basiswaarde 10 te berekenen. Als het meest linkse bit een 1 is, is het aantal negatief, dus nemen we het complement van het getal van de twee om de positieve waarde te krijgen en voegen we eenvoudig een negatief teken toe.
Als we bijvoorbeeld 11110010 hebben, weten we dat dit een negatief getal is. We kunnen het complement van twee vinden door alle cijfers links van de meest rechtse 1 om te draaien, en geeft ons 00001110. Dit staat voor 13, dus we weten dat 11110010 gelijk is aan -13.
We zijn weer terug bij de bitsgewijze operatoren en de volgende is de bitwise XOR-operator. Er is geen equivalente booleaanse operator voor deze.
De operator ^ lijkt op de & en | operators in dat het duurt een int
of uint
aan beide kanten. Wanneer het het resulterende getal berekent, vergelijkt het opnieuw de binaire cijfers van deze nummers. Als de een of de ander een 1 is, wordt er een 1 ingevoegd in het resultaat, anders wordt er een 0 ingevoegd. Hier komt de naam XOR of "exclusief of" vandaan.
Laten we eens kijken naar ons gebruikelijke voorbeeld:
De operator ^ heeft wel toepassingen - het is vooral goed voor het omschakelen van binaire cijfers - maar we zullen geen praktische toepassingen in dit artikel behandelen.
We zijn nu op de bitshift-operators, met name de bitwise shift-operator hier.
Deze werken een beetje anders dan voorheen. In plaats van twee gehele getallen te vergelijken, zoals &, |, en ^ did, verplaatsen deze operators een geheel getal. Aan de linkerkant van de operator staat het gehele getal dat wordt verplaatst en aan de rechterkant ziet u hoeveel er moet worden verschoven. Dus bijvoorbeeld 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.
Laten we dit voorbeeld eens bekijken (onthoud, we gaan net doen alsof gehele getallen slechts 8 bits hebben in plaats van 32). Hier hebben we het nummer 37 zittend op zijn mooie blok van geheugen 8 bits breed.
Oké, laten we alle cijfers naar links schuiven met 3, zoals 37 << 3
zou doen:
Maar nu hebben we een klein probleem - wat doen we met de 3 open stukjes geheugen waarin we de cijfers hebben verplaatst?
Natuurlijk! Lege plekken worden gewoon vervangen door 0's. We eindigen met 00101000. En dat is alles wat er is aan de linker bitshift. Houd er rekening mee dat Flash altijd denkt dat het resultaat van een linkse bitshift een is int
, geen a uint
. Dus als je een nodig hebt uint
om een of andere reden moet je het casten naar a uint
zoals dit: uint (37 << 3)
. Deze casting verandert eigenlijk niets aan de binaire informatie, maar hoe Flash het interpreteert (het complement van de hele twee).
Een interessante eigenschap van de linker bitshift is dat het hetzelfde is als het vermenigvuldigen van een cijfer met twee naar de shiftAmount-de kracht. Zo, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8
. Als u de linkerverschuiving kunt gebruiken in plaats van Math.pow
, je zult een enorme prestatieverhoging zien.
Het is je misschien opgevallen dat het binaire getal waarmee we eindigden niet gelijk was aan 37 * 8. Dit komt door ons gebruik van slechts 8 bits geheugen voor gehele getallen; als u het in ActionScript probeert, krijgt u het juiste resultaat. Of probeer het met de demo bovenaan de pagina!
Nu we de linker bitshift begrijpen, zal de volgende, de juiste bitshift, gemakkelijk zijn. Alles schuift naar rechts het bedrag dat we opgeven. Het enige kleine verschil is wat de lege stukjes worden gevuld met.
Als we beginnen met een negatief getal (een binair getal waarbij het meest linkse bit een 1 is), zijn alle lege velden gevuld met een 1. Als we beginnen met een positief getal (waar het meest linkse deel, of het meest significante bit, is een 0), dan zijn alle lege velden gevuld met een 0. Nogmaals, dit alles gaat terug naar het complement van twee.
Hoewel dit ingewikkeld klinkt, behoudt het eigenlijk alleen het teken van het nummer waarmee we beginnen. Zo -8 >> 2 == -2
terwijl 8 >> 2 == 2
. Ik zou aanraden om die zelf op papier uit te proberen.
Aangezien >> het tegenovergestelde is van <<, 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
, je krijgt een aanzienlijke prestatieverbetering.
Onze laatste bitsgewijze operator is de bitwise unsigned right shift. Dit lijkt erg op de normale bitsgewijze verplaatsing naar rechts, behalve dat alle lege bits aan de linkerkant gevuld zijn met nullen. Dit betekent dat het resultaat van deze operator altijd een positief geheel getal is en het behandelt altijd het gehele getal dat wordt verschoven als een niet-ondertekend geheel getal. We zullen hier in dit gedeelte geen voorbeeld van bespreken, maar we zullen er binnenkort een gebruik van zien.
Een van de meest praktische toepassingen van bitsgewijze operatoren in Actionscript 3 is het werken met kleuren, die doorgaans worden opgeslagen als uints
.
Het standaardformaat voor kleuren is om ze in hexadecimaal te schrijven: 0xAARRGGBB - elke letter staat voor een hexadecimaal cijfer. Hier geven de eerste twee hexadecimale cijfers, die gelijk zijn aan de eerste acht binaire cijfers, onze alpha of transparantie weer. De volgende acht bits vertegenwoordigen de hoeveelheid rood in onze kleur (dus een geheel getal van 0 tot 255), de volgende acht de hoeveelheid groen en de laatste acht vertegenwoordigen de hoeveelheid blauw in onze kleur.
Zonder bitwise-operators is het bijzonder moeilijk om in dit formaat met kleuren te werken, maar met hen is het eenvoudig!
Uitdaging 1: zoek de hoeveelheid blauw in een kleur: Zoek met behulp van de operator & de hoeveelheid blauw in een willekeurige kleur.
public function findBlueComponent (color: uint): uint // Uw code hier!
We hebben een manier nodig om alle andere gegevens te 'wissen' of te maskeren kleur
en heb gewoon de blauwe component over. Dit is gemakkelijk, eigenlijk! Als we nemen kleur & 0x000000FF
- of, eenvoudiger geschreven, kleur & 0xFF
- we hebben alleen de blauwe component.
Zoals je hierboven kunt zien en je hebt geleerd in de beschrijving van de operator &, is elk binair cijfer & 0 altijd gelijk aan 0, terwijl elk binair cijfer & 1 zijn waarde behoudt. Dus als we onze kleur maskeren met 0xFF, die alleen maar enen bevat waar de blauwe component van onze kleur zich bevindt, krijgen we alleen de blauwe component.
Uitdaging 2: Zoek de hoeveelheid rood in een kleur: Probeer met behulp van twee bitsgewijze operatoren de hoeveelheid rood in een willekeurige kleur te vinden.
public function findRedComponent (color: uint): uint // Uw code hier!
We hebben eigenlijk twee oplossingen voor dit probleem. Eentje zou dat zijn return (kleur & 0xFF0000) >> 16;
en de andere zou zijn return (kleur >> 16) & 0xFF;
Dit lijkt erg op Challenge 1, behalve dat we ons antwoord op een gegeven moment moeten verplaatsen.
Uitdaging 3: Vind de transparantie van een kleur: Zoek met behulp van slechts één bitsgewijze operator de alpha van een kleur (een geheel getal van 0 tot 255).
public function findAlphaComponent (color: uint): uint // Uw code hier!
Deze is een beetje lastiger. We moeten voorzichtig zijn met welke rechtse shift-operator we kiezen. Omdat we werken met de meest linkse cijfers van a uint
, we willen >>> gebruiken. Ons antwoord is dus eenvoudig retourkleur >>> 24;
.
Laatste uitdaging: maak een kleur van zijn componenten: De ... gebruiken << and | operators, take the components of a color and merge them in to one uint
.
openbare functie createColor (a: uint, r: uint, g: uint, b: uint): uint // Uw code hier!
Hier moeten we elk onderdeel naar de juiste positie verplaatsen en vervolgens samenvoegen. We willen dat Flash het als een geheel getal zonder teken behandelt, dus we hebben het naar a uint
: return uint ((a << 24) | (r << 16) | (g << 8) | b);
Je hebt misschien gemerkt dat ik heb nagelaten om de bitwise-operators in de compound uit te leggen. Stel je voor dat we een integer x hebben. Dan, x = x & 0xFF
is hetzelfde als x & = 0xFF
, x = x | 256
is hetzelfde als x | = 256
, en zo verder voor de rest van de samengestelde operators.
Bedankt voor het lezen van dit artikel! Ik hoop dat je bitwise-operators nu begrijpt en ze kunt gebruiken in je AS3-code (of in veel andere talen!). Laat zoals altijd, als u vragen of opmerkingen heeft, deze hieronder achter.