Bitweise Operatoren verstehen

Bitweise Operatoren sind die seltsam aussehenden Operatoren, die möglicherweise schwer zu verstehen scheinen… aber nicht mehr! Dieser einfach zu verfolgende Artikel hilft Ihnen zu verstehen, was sie sind und wie sie verwendet werden sollen, und zeigt anhand einiger praktischer Beispiele, wann und warum Sie sie benötigen.


Einführung

Bitweise Operatoren sind Operatoren (genau wie +, *, && usw.), die operieren ints und uints auf der binären Ebene. Das heißt, sie schauen direkt auf die Binärziffern oder Bits einer Ganzzahl. Das klingt alles furchterregend, aber in Wahrheit sind bitweise Operatoren sehr einfach zu bedienen und auch sehr nützlich!

Es ist jedoch wichtig, dass Sie Binär- und Hexadezimalzahlen verstehen. Wenn Sie dies nicht tun, lesen Sie bitte diesen Artikel - er wird Ihnen wirklich helfen! Nachfolgend finden Sie eine kleine Anwendung, mit der Sie die verschiedenen bitweisen Operatoren ausprobieren können.

Mach dir keine Sorgen, wenn du noch nicht verstehst, was los ist, alles wird bald klar sein…


Bitweise Operatoren erkennen

Schauen wir uns die bitweisen Operatoren an, die AS3 bereitstellt. Viele andere Sprachen sind sich sehr ähnlich (beispielsweise haben JavaScript und Java praktisch identische Operatoren):

  • & (bitweises AND)
  • | (bitweises ODER)
  • ~ (bitweise NICHT)
  • ^ (bitweises XOR)
  • << (bitwise left shift)
  • >> (bitweise Verschiebung nach rechts)
  • >>> (bitweise vorzeichenlose Rechtsverschiebung)
  • & = (bitweise UND-Zuordnung)
  • | = (bitweise ODER-Zuordnung)
  • ^ = (bitweise XOR-Zuordnung)
  • <<= (bitwise left shift and assignment)
  • >> = (bitweise Rechtsverschiebung und Zuordnung)
  • >>> = (bitweise vorzeichenlose Rechtsverschiebung und Zuweisung)

Es gibt ein paar Dinge, die Sie daraus ziehen sollten: Erstens sehen einige bitweise Operatoren den Operatoren ähnlich, die Sie zuvor verwendet haben (& vs. &&, | vs. ||). Das ist weil sie sind etwas ähnlich.

Zweitens kommen die meisten bitweisen Operatoren mit einem zusammengesetztes Zuweisungsformular von ihnen selbst. Das ist das Gleiche wie Sie + und + =, - und - = usw benutzen können.


Der & Betreiber

Zuerst geht es weiter: der bitweise AND-Operator &. Ein kurzes Heads-Up: Normalerweise, ints und uints beanspruchen 4 Byte oder 32 Bit Speicherplatz. Das bedeutet jeder int oder uint wird als 32 Binärziffern gespeichert. Im Interesse dieses Tutorials geben wir das manchmal vor ints und uints Nehmen Sie nur 1 Byte in Anspruch und haben nur 8 Binärstellen.

Der Operator & vergleicht jede Binärziffer von zwei ganzen Zahlen und gibt eine neue Ganzzahl mit einer 1 zurück, wenn beide Zahlen an einer anderen Stelle eine 1 und eine 0 hatten. Ein Diagramm sagt mehr als tausend Worte, hier ist eines, um die Dinge zu klären. Es repräsentiert das Tun 37 & 23, was gleich ist 5.

Beachten Sie, wie jede binäre Ziffer von 37 und 23 verglichen wird, und das Ergebnis hat eine 1, wenn sowohl 37 als auch 23 eine 1 haben, und das Ergebnis hat andernfalls eine 0.

Eine gängige Denkweise für binäre Ziffern ist as wahr oder falsch. Das heißt, 1 ist äquivalent zu wahr und 0 ist äquivalent zu falsch. Dies macht den Operator & sinnvoller.

Wenn wir zwei Boolesche vergleichen, tun wir das normalerweise boolean1 && boolean2. Dieser Ausdruck gilt nur für beide Fälle boolean1 und boolean2 sind wahr. Auf die gleiche Weise, Ganzzahl1 & Ganzzahl2 ist äquivalent, da der Operator & nur eine 1 ausgibt, wenn beide Binärziffern unserer beiden Ganzzahlen 1 sind.

Hier ist eine Tabelle, die diese Idee repräsentiert:

Eine nette kleine Verwendung des & -Bedieners besteht darin, zu prüfen, ob eine Zahl gerade oder ungerade ist. Bei Ganzzahlen können Sie einfach das ganz rechte Bit (auch als das niedrigstwertige Bit bezeichnet) prüfen, um festzustellen, ob die Ganzzahl ungerade oder gerade ist. Dies liegt daran, dass beim Konvertieren in die Basis 10 das Bit ganz rechts 2 darstellt0 oder 1. Wenn das Bit ganz rechts 1 ist, wissen wir, dass unsere Anzahl ungerade ist, da wir 1 zu einem Bündel von Zweierpotenzen hinzufügen, das immer gerade sein wird. Wenn das Bit ganz rechts 0 ist, wissen wir, dass unsere Zahl gerade sein wird, da es einfach aus mehreren geraden Zahlen besteht.

Hier ist ein Beispiel:

 var randInt: int = int (Math.random () * 1000); if (randInt & 1) trace ("Ungerade Zahl.");  else trace ("Gerade Anzahl."); 

Auf meinem Computer war diese Methode etwa 66% schneller als bei Verwendung randInt% 2 auf gerade und ungerade Zahlen prüfen. Das ist schon ein Leistungsschub!


Die | Operator

Als nächstes folgt der bitweise OR-Operator |. Wie Sie vielleicht schon erraten haben, ist das | Operator ist auf die || Operator wie der Operator & ist für den Operator &&. Die | Der Operator vergleicht jede Binärzahl über zwei Ganzzahlen und gibt eine 1 zurück, wenn entweder von ihnen sind 1. Wieder ähnelt dies dem || Operator mit Booleschen.

Schauen wir uns das gleiche Beispiel wie zuvor an, außer jetzt mit | Operator anstelle des Operators &. Wir machen jetzt 37 | 23 was gleich 55 ist:


Flags: Verwendung von & und | Betreiber

Wir können das & und | nutzen Operatoren, damit wir einer Funktion mehrere Optionen in einer einzigen Funktion übergeben können int.

Sehen wir uns eine mögliche Situation an. Wir bauen eine Popup-Fensterklasse. Unten können wir die Schaltflächen Ja, Nein, Okay oder Abbrechen oder eine Kombination davon haben. Wie sollen wir das tun? Hier ist der harte Weg:

 public class PopupWindow erweitert Sprite // Variablen, Konstruktor usw. ... public static void showPopup (yesButton: Boolean, noButton: Boolean, okayButton: Boolean, cancelButton: Boolean) if (yesButton) // Schaltfläche YES hinzufügen if (noButton) ) // add NO-Button // und so weiter für die restlichen Buttons

Ist das schrecklich? Nein. Es ist jedoch schlecht, wenn Sie Programmierer sind, wenn Sie bei jedem Aufruf der Funktion die Reihenfolge der Argumente nachschlagen müssen. Das ist auch ärgerlich. Wenn Sie beispielsweise nur die Schaltfläche Abbrechen anzeigen möchten, müssen Sie alle anderen Einstellungen vornehmen Booleaner zu falsch.

Verwenden wir das, was wir über & und | gelernt haben um eine bessere Lösung zu finden:

 public class PopupWindow erweitert Sprite public static const YES: int = 1; public static const NO: int = 2; public static const OKAY: int = 4; public static const CANCEL: int = 8; public static void showPopup (Schaltflächen: int) if (Schaltflächen & JA) // JA-Schaltfläche hinzufügen if (Schaltflächen & NEIN) // NEIN-Schaltfläche hinzufügen

Wie würde ein Programmierer die Funktion aufrufen, so dass die Schaltflächen Ja, Nein und Abbrechen angezeigt werden? So was:

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

Was ist los? Es ist wichtig zu beachten, dass unsere Konstanten im zweiten Beispiel nur Zweierpotenzen sind. Wenn wir uns ihre binären Formen anschauen, werden wir feststellen, dass alle eine Ziffer gleich 1 haben und der Rest gleich 0. Tatsächlich haben sie jeweils eine andere Ziffer gleich 1. Dies bedeutet, egal wie wir miteinander kombinieren Wenn Sie mit | versehen sind, wird uns jede Kombination eine eindeutige Nummer geben. Wir betrachten es anders, unser Ergebnis Anweisung wird eine binäre Zahl mit einer 1 sein, wenn unsere Optionen eine 1 hatten.

Für unser aktuelles Beispiel haben wir PopupWindow.YES | PopupWindow.NO | PopupWindow.CANCEL das ist äquivalent zu 1 | 2 | 8 was binär umgeschrieben ist 00000001 | 00000010 | 00001000 das gibt uns ein Ergebnis von 00001011.

Jetzt in unserem Popup zeigen() Funktion verwenden wir &, um zu prüfen, welche Optionen übergeben wurden. Zum Beispiel beim Prüfen Tasten & JA, Alle Bits in YES sind gleich 0, außer dem ganz rechten. Wir prüfen also im Wesentlichen, ob das ganz rechte Bit in den Schaltflächen eine 1 ist oder nicht. Wenn es so ist, Tasten & JA wird nicht gleich 0 und alles in der if-Anweisung wird ausgeführt. Umgekehrt, wenn das ganz rechte Bit in den Schaltflächen 0 ist, Tasten & JA wird gleich 0 und die if-Anweisung wird nicht ausgeführt.


Der ~ Operator

Der bitweise NOT-Operator unterscheidet sich geringfügig von den beiden, die wir bisher betrachtet haben. Anstatt auf jeder Seite eine Ganzzahl zu nehmen, wird nur eine Ganzzahl danach genommen. Das ist genau wie das! Operator, und es ist nicht überraschend, dass es eine ähnliche Sache tut. In der Tat genauso wie! wirft einen boolean aus wahr zu falsch oder umgekehrt: Der Operator ~ kehrt jede binäre Ziffer in einer Ganzzahl um: von 0 nach 1 und von 1 nach 0:

Ein kurzes Beispiel. Nehmen wir an, wir haben die ganze Zahl 37 oder 00100101. ~ 37 ist dann 11011010. Was ist der Basiswert 10 davon? Gut…


Zwei-Komplement, uint vs. int, und mehr!

Nun beginnt der Spaß! Wir werden uns Binärzahlen auf einem Computer genauer ansehen. Beginnen wir mit dem uint. Wie bereits erwähnt, a uint ist normalerweise 4 Byte oder 32 Bit lang, was bedeutet, dass es 32 Binärziffern hat. Das ist leicht zu verstehen: Um den Wert der Basis 10 zu erhalten, konvertieren Sie die Zahl einfach regelmäßig in die Basis 10. Wir bekommen immer eine positive Zahl.

Aber wie wäre es mit dem int? Es verwendet auch 32 Bit, aber wie werden negative Zahlen gespeichert? Wenn Sie vermuteten, dass die erste Ziffer zum Speichern des Zeichens verwendet wird, sind Sie auf dem richtigen Weg. Werfen wir einen Blick auf die Zweierkomplement System zum Speichern von Binärzahlen. Während wir hier nicht auf alle Details eingehen, wird ein Zwei-Komplement-System verwendet, da es die binäre Arithmetik vereinfacht.

Um das Zweierkomplement einer binären Zahl zu finden, drehen wir einfach alle Bits um (d. H., Was der Operator ~ tut) und fügen dem Ergebnis eins hinzu. Lass uns das einmal ausprobieren:

Wir definieren dann unser Ergebnis als Wert -37. Warum machen Sie diesen komplizierten Prozess und nicht nur das allererste Bit und rufen Sie -37 auf?

Nehmen wir einen einfachen Ausdruck 37 + -37. Wir alle wissen, dass dies gleich 0 sein sollte, und wenn wir die 37 zu ihrem Zweierkomplement hinzufügen, erhalten wir Folgendes:

Beachten Sie, dass, da unsere Ganzzahlen nur acht Binärziffern enthalten, die 1 in unserem Ergebnis verworfen wird und wir bei 0 landen, wie wir sollten.

Um das Negativ einer Zahl zu finden, nehmen wir einfach das Zweierkomplement. Wir können dies tun, indem wir alle Bits invertieren und eins hinzufügen.

Willst du das selbst ausprobieren? Hinzufügen Spur (~ 37 + 1); in eine AS3-Datei, dann kompilieren und ausführen Wie Sie sehen, wird -37 gedruckt.

Es gibt auch eine kleine Abkürzung, um dies von Hand zu tun: Beginnen Sie von rechts nach links, bis Sie eine 1 erreichen. Drehen Sie alle Bits nach links von dieser ersten 1.

Wenn wir uns eine binäre Zahl mit Vorzeichen anschauen (mit anderen Worten, eine, die negativ sein kann, eine int kein uint), können wir die Ziffer ganz links betrachten, um festzustellen, ob es negativ oder positiv ist. Wenn es eine 0 ist, ist die Zahl positiv und wir können die Basis 10 einfach konvertieren, indem wir ihren Basiswert 10 berechnen. Wenn das äußerste linke Bit eine 1 ist, dann ist die Zahl negativ, also nehmen wir das Zweierkomplement der Zahl, um den positiven Wert zu erhalten, und fügen dann einfach ein negatives Vorzeichen hinzu.

Wenn wir beispielsweise 11110010 haben, wissen wir, dass es eine negative Zahl ist. Wir finden das Zweierkomplement, indem wir alle Ziffern links von der rechten 1 umdrehen und 00001110 angeben. Dies entspricht 13, also wissen wir, dass 11110010 -13 ist.


Der ^ Operator

Wir sind zurück zu den bitweisen Operatoren und als nächstes kommt der bitweise XOR-Operator. Es gibt keinen äquivalenten booleschen Operator zu diesem.

Der ^ Operator ähnelt dem & und | Betreiber, dass es dauert ein int oder uint auf beiden Seiten. Bei der Berechnung der resultierenden Zahl werden die Binärziffern dieser Zahlen erneut verglichen. Wenn der eine oder der andere eine 1 ist, wird eine 1 in das Ergebnis eingefügt, ansonsten wird eine 0 eingefügt. Hier kommt der Name XOR oder "exklusiv oder".

Schauen wir uns unser übliches Beispiel an:

Der ^ -Operator kann verwendet werden - er eignet sich besonders für das Umschalten von Binärziffern - wir werden in diesem Artikel jedoch keine praktischen Anwendungen behandeln.


Das << Operator

Wir befinden uns jetzt auf den Bitshiftoperatoren, insbesondere auf dem bitweisen Linksverschiebungsoperator.

Diese arbeiten etwas anders als zuvor. Anstatt zwei Ganzzahlen wie &, | und ^ zu vergleichen, verschieben diese Operatoren eine Ganzzahl. Auf der linken Seite des Operators befindet sich die ganze Zahl, die verschoben wird, und auf der rechten Seite wird angezeigt, um wie viel verschoben werden soll. So zum Beispiel 37 << 3 is shifting the number 37 to the left by 3 places. Of course, we're working with the binary representation of 37.

Schauen wir uns dieses Beispiel an (denken Sie daran, dass wir nur vorgeben, dass ganze Zahlen nur 8 Bits anstelle von 32 haben). Hier haben wir die Nummer 37 auf ihrem schönen 8-Bit-Speicherblock.

Okay, lass uns alle Ziffern um 3 nach links schieben 37 << 3 würdest du:

Aber jetzt haben wir ein kleines Problem - was machen wir mit den 3 offenen Bits des Speichers, aus denen wir die Ziffern verschoben haben?

Na sicher! Alle leeren Stellen werden einfach durch 0 ersetzt. Wir landen mit 00101000. Und das ist alles für die linke Bitverschiebung. Denken Sie daran, dass Flash immer denkt, dass das Ergebnis einer linken Bitverschiebung ein ist int, kein uint. Wenn Sie also eine brauchen uint Aus irgendeinem Grund müssen Sie es in ein gießen uint so was: uint (37 << 3). Dieses Casting ändert nichts an den binären Informationen, sondern nur, wie Flash sie interpretiert (die beiden Komplemente)..

Ein interessantes Merkmal der linken Bitverschiebung besteht darin, dass sie mit dem Multiplizieren einer Zahl mit zwei zur shiftAmount-ten Potenz identisch ist. So, 37 << 3 == 37 * Math.pow(2,3) == 37 * 8. Wenn Sie die linke Verschiebung anstelle von verwenden können Math.pow, Sie werden eine enorme Leistungssteigerung sehen.

Möglicherweise haben Sie bemerkt, dass die Binärzahl, mit der wir am Ende lagen, nicht 37 * 8 entsprach. Dies liegt nur daran, dass wir nur 8 Bit Speicher für Ganzzahlen verwenden. Wenn Sie es in ActionScript versuchen, erhalten Sie das korrekte Ergebnis. Oder probieren Sie es mit der Demo oben auf der Seite!


Der >> Operator

Nun, da wir die linke Bitverschiebung verstehen, wird die nächste, die rechte Bitverschiebung leicht sein. Alles schiebt sich um den Betrag, den wir angeben. Der einzige kleine Unterschied besteht darin, womit die leeren Bits gefüllt werden.

Wenn wir mit einer negativen Zahl beginnen (eine binäre Zahl, bei der das ganz linke Bit eine 1 ist), werden alle leeren Felder mit einer 1 gefüllt. Wenn wir mit einer positiven Zahl beginnen (wobei das ganz linke Bit oder das höchstwertige ist) Bit, ist eine 0), dann werden alle leeren Felder mit einer 0 gefüllt. Wieder geht alles auf das Zweierkomplement zurück.

Während dies kompliziert klingt, behält es im Grunde nur das Vorzeichen der Zahl, mit der wir beginnen. So -8 >> 2 == -2 während 8 >> 2 == 2. Ich würde empfehlen, diese auf Papier selbst auszuprobieren.

Da >> das Gegenteil von ist <<, 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, Sie erhalten eine deutliche Leistungssteigerung.


Der >>> Operator

Unser letzter bitweiser Operator ist die bitweise vorzeichenlose Rechtsverschiebung. Dies ist der normalen bitweisen Rechtsverschiebung sehr ähnlich, mit der Ausnahme, dass alle leeren Bits auf der linken Seite mit 0 gefüllt sind. Das bedeutet, dass das Ergebnis dieses Operators immer eine positive Ganzzahl ist und die verschobene Ganzzahl immer als vorzeichenlose Ganzzahl behandelt wird. Wir werden in diesem Abschnitt kein Beispiel dafür durchgehen, aber wir werden in Kürze eine Verwendung dafür sehen.


Verwenden von bitweisen Operatoren zum Arbeiten mit Farben

Eine der praktischsten Anwendungen von bitweisen Operatoren in Actionscript 3 ist das Arbeiten mit Farben, die normalerweise als gespeichert werden uints.

Das Standardformat für Farben besteht darin, sie hexadezimal zu schreiben: 0xAARRGGBB - Jeder Buchstabe steht für eine hexadezimale Ziffer. Hier stellen die ersten beiden Hexadezimalziffern, die den ersten acht Binärziffern entsprechen, unser Alpha oder Transparenz dar. Die nächsten acht Bits stehen für die Menge an Rot in unserer Farbe (also eine ganze Zahl von 0 bis 255), die nächsten acht für die Menge an Grün und die letzten acht für die Menge an Blau in unserer Farbe.

Ohne bitweise Operatoren ist es extrem schwierig, mit Farben in diesem Format zu arbeiten - aber mit ihnen ist es einfach!

Herausforderung 1: Finden Sie die Menge an Blau in einer Farbe: Versuchen Sie mit dem Operator & den Blauanteil in einer beliebigen Farbe zu ermitteln.

 öffentliche Funktion findBlueComponent (Farbe: uint): uint // Ihr Code hier! 

Wir brauchen eine Möglichkeit, alle anderen Daten in "zu löschen" oder zu maskieren Farbe und nur noch die blaue Komponente übrig. Das ist eigentlich ganz einfach! Wenn wir nehmen Farbe & 0x000000FF - oder einfacher geschrieben, Farbe & 0xFF - wir haben nur noch die blaue Komponente.

Wie Sie oben sehen und in der Beschreibung des Operators & erfahren haben, ist jede binäre Ziffer & 0 immer gleich 0, während jede binäre Ziffer & 1 ihren Wert beibehält. Wenn wir also unsere Farbe mit 0xFF maskieren, die nur 1s enthält, wo sich die blaue Komponente unserer Farbe befindet, erhalten wir nur die blaue Komponente.

Herausforderung 2: Bestimmen Sie die Menge an Rot in einer Farbe: Versuchen Sie mit zwei bitweisen Operatoren, den Rotanteil in einer beliebigen Farbe zu ermitteln.

 öffentliche Funktion findRedComponent (Farbe: uint): uint // Ihr Code hier! 

Wir haben eigentlich zwei Lösungen für dieses Problem. Einer wäre Rückgabe (Farbe & 0xFF0000) >> 16; und der andere wäre return (Farbe >> 16) & 0xFF;

Dies ist sehr ähnlich zu Herausforderung 1, nur dass wir unsere Antwort irgendwann verschieben müssen.

Herausforderung 3: Finden Sie die Transparenz einer Farbe: Versuchen Sie mit nur einem bitweisen Operator das Alpha einer Farbe (eine ganze Zahl von 0 bis 255) zu finden..

 öffentliche Funktion findAlphaComponent (Farbe: uint): uint // Ihr Code hier! 

Dies ist ein bisschen kniffliger. Wir müssen vorsichtig sein, mit welchem ​​Rechtsschichtbetreiber wir wählen. Weil wir mit den linken Ziffern von a arbeiten uint, wir möchten den Operator >>> verwenden. Unsere Antwort lautet also einfach Rückkehrfarbe >>> 24;.

Letzte Herausforderung: Erstellen Sie eine Farbe aus ihren Komponenten: Verwendung der << and | operators, take the components of a color and merge them in to one uint.

 öffentliche Funktion createColor (a: uint, r: uint, g: uint, b: uint): uint // Ihr Code hier! 

Hier müssen wir jede Komponente an die richtige Position verschieben und dann zusammenführen. Wir möchten, dass Flash es als eine vorzeichenlose Ganzzahl behandelt, also setzen wir es in eine uint: Rückkehr uint ((a << 24) | (r << 16) | (g << 8) | b);


Verbundoperatoren

Möglicherweise haben Sie bemerkt, dass ich die zusammengesetzten bitweisen Operatoren nicht erklärt habe. Stellen Sie sich vor, wir haben eine ganze Zahl x. Dann, x = x & 0xFF ist das gleiche wie x & = 0xFF, x = x | 256 ist das gleiche wie x | = 256, und so weiter für den Rest der Verbundoperatoren.


Fazit

Vielen Dank für das Lesen dieses Artikels! Ich hoffe, Sie verstehen jetzt bitweise Operatoren und können diese in Ihrem AS3-Code (oder in vielen anderen Sprachen!) Verwenden. Wenn Sie Fragen oder Anmerkungen haben, lassen Sie sie bitte unten.