Bitweiser Operator

aus Wikipedia, der freien Enzyklopädie
Wechseln zu: Navigation, Suche

In der Informatik ist ein bitweiser Operator ein Operator, der auf ein oder zwei Bitfolgen oder Binärzahlen auf der Ebene einzelner Bits angewendet wird. Auf vielen Computern sind bitweise Operationen etwas schneller als Additions- und Subtraktionsoperationen und deutlich schneller als Multiplikations- und Divisionsoperationen.

Bitweise Operatoren[Bearbeiten]

NICHT[Bearbeiten]

Ein bitweises NICHT oder Komplement ist eine einstellige Verknüpfung, die eine logische Negation jedes Bits durchführt und damit das Einerkomplement einer Binärzahl bildet. Jede 0 wird durch eine 1 ausgetauscht und umgekehrt. Beispiel:

NICHT 0111
    = 1000

In vielen Programmiersprachen der C-Familie wird das bitweise NICHT als „~“ (Tilde) dargestellt. Dieser Operator darf nicht mit dem Operator für „logisches NICHT“ „!“ (Ausrufezeichen) verwechselt werden, der den gesamten Wert als Booleschen Ausdruck interpretiert und true oder false zurückgibt. Das „logische NICHT“ ist keine bitweise Operation.

NICHT (Logisch): !1d = 0d; !5d = 0d
NICHT (Bitweise): ~1d = 0d; ~5d = ~0101b = 1010b = 10d

ODER[Bearbeiten]

Ein bitweises ODER wird auf zwei Bitfolgen gleicher Länge angewendet und gibt eine Bitfolge derselben Länge zurück, indem es jeweils Bits an der gleichen Stelle (jeweils das erste Bit, jeweils das zweite Bit usw.) mit einem logischen ODER (logische Disjunktion) verknüpft. Bei jedem Paar ist das Ergebnisbit 1, falls das Bit der ersten Bitfolge 1 ist ODER das Bit der zweiten Bitfolge 1 ist, ansonsten ist das Ergebnisbit 0. Beispiel:

     0101
ODER 0011
   = 0111

In den mit C verwandten Programmiersprachen wird das bitweise ODER durch „|“ (senkrechter Strich) dargestellt. Dieser Operator darf nicht mit seinem booleschen Gegenstück verwechselt werden, das seine Operanden als boolesche Werte interpretiert und als „||“ (zwei senkrechte Striche) dargestellt wird.

Das bitweise ODER wird verwendet, wenn mehrere Bits als Flags verwendet werden; die Bits einer einzelnen Binärzahl können jeweils eine eigene boolesche Variable darstellen. Wendet man das bitweise ODER auf einen solchen Binärwert und eine „Maske“ an, die an bestimmten Stellen eine 1 enthält, so erhält man eine neue Bitfolge, in der diese Bits zusätzlich zu den ursprünglich vorhandenen gesetzt sind. Beispiel:

0010

kann als Liste von vier Flags angesehen werden. Das erste, zweite und vierte Flag sind nicht gesetzt (0), das dritte Flag ist gesetzt (1). Das erste Flag kann gesetzt werden, indem man diese Bitfolge mit einer Bitfolge verknüpft, die nur an der ersten Stelle eine 1 hat:

   0010
OR 1000
 = 1010

Diese Technik wird eingesetzt, um Speicherplatz zu sparen, wenn Programme sehr viele Boolesche Werte verwalten müssen.

XOR[Bearbeiten]

Ein bitweises exklusives ODER wird auf zwei Bitfolgen der gleichen Länge angewendet und führt die logische XOR-Operation auf jedem Paar korrespondierender Bits durch. Das Ergebnisbit ist 1, falls die zwei Bits unterschiedlich sind, und 0, falls sie gleich sind. Beispiel:

    0101
XOR 0011
  = 0110

In den mit C verwandten Programmiersprachen wird das bitweise XOR als „^“ (Circumflex) dargestellt.

In der Assemblersprache wird das bitweise XOR gelegentlich eingesetzt, um den Wert eines Prozessorregisters auf 0 zu setzen. Wendet man XOR auf zwei identische Operanden an, so erhält man immer 0. In vielen Architekturen benötigt diese Operation weniger Rechenzeit, als man für das Laden einer 0 und das Speichern im Register benötigt.

Das bitweise XOR kann auch verwendet werden, um Flags in Bitfolgen umzuschalten. Beispiel:

0010

Das erste und das dritte Bit können simultan umgeschaltet werden, indem man diese Bitfolge mit XOR mit einer Maske verknüpft, welche die Bits 1 und 3 gesetzt hat:

    0010
XOR 1010
  = 1000

Diese Technik kann eingesetzt werden, um Bitfolgen zu manipulieren, die mehrere boolesche Variablen repräsentieren.

UND[Bearbeiten]

Ein bitweises UND wird auf zwei Bitfolgen gleicher Länge angewendet und führt die logische UND-Verknüpfung (logische Konjunktion) auf jedem Paar korrespondierender Bits durch. Das Ergebnisbit ist 1, falls beide Bits 1 sind, ansonsten 0. Beispiel:

    0101
UND 0011
  = 0001

In den mit C verwandten Programmiersprachen wird das bitweise UND durch „&“ (kaufmännisches Und, engl. 'ampersand') dargestellt. Dieser Operator darf nicht mit seinem booleschen Gegenstück verwechselt werden, das seine Operanden als boolesche Werte interpretiert und als „&&“ (zwei kaufmännische Und) dargestellt wird.

Das bitweise UND kann verwendet werden, um eine Bitfolge zu maskieren. Dadurch können Teile eines Bitstrings isoliert werden, und man kann bestimmen, ob ein bestimmtes Bit gesetzt ist oder nicht. Beispiel:

0011

Um herauszufinden, ob das dritte Bit gesetzt ist oder nicht, wird darauf ein bitweises UND mit einer Maske angewendet, die an der dritten Position eine 1 enthält:

    0011
UND 0010
  = 0010

Da das Ergebnis nicht Null ist, muss das dritte Bit in der ursprünglichen Bitfolge eine 1 gewesen sein. Diese Anwendung des bitweisen UND wird bitweise Maskierung genannt, weil Teile, die nicht geändert werden sollen oder für die Berechnung nicht wichtig sind, ausgeblendet werden.

Das bitweise UND kann mit dem bitweisen NICHT kombiniert werden, um Bits zu löschen. Wir möchten beispielsweise das zweite Bit (auch Flag) folgender Bitfolge löschen (d.h. auf 0 setzen):

0110 

Dies geschieht indem wir eine invertierte Maske (die Null muss dafür an der Stelle der zu ändernden Ziffer gesetzt werden) auf unsere Bitfolge anwenden. Invertieren können wir mit dem NICHT-Operator.

NICHT 0100
    = 1011

Danach wird die Bitfolge und die Maske mittels UND-Operator verknüpft:

      0110
  UND 1011
    = 0010

Weiterhin ist es mit dem bitweisen UND möglich, eine n-Bit-Zahl modulo 2k zu rechnen, indem man sie mit 2k-1 verundet. Dadurch werden alle Bits ab dem k plus ersten Bit auf 0 gesetzt, was dann genau dem Ergebnis der modulo-Berechnung entspricht.

Beispiel: 17 mod 8 = 1 entspricht

      10001       (17)
  UND 00111       (7 = 8-1)
    = 00001

Bitweise Verschiebungen[Bearbeiten]

Dieser Artikel oder Abschnitt bedarf einer Überarbeitung. Näheres ist auf der Diskussionsseite angegeben. Hilf mit, ihn zu verbessern, und entferne anschließend diese Markierung.

Bitweise Verschiebungen (engl. bitwise shift) werden manchmal ebenfalls als bitweise Operationen aufgefasst, weil sie auf der binären Ebene statt auf dem numerischen Wert einer Variable arbeiten; bitweise Verschiebungen arbeiten jedoch nicht auf Paaren von korrespondierenden Bits und sind damit nicht bitweise im ursprünglichen Sinn. Schaltungstechnisch können bitweise Verschiebungen und Rotationen um eine beliebige Stellenanzahl in Form von Barrel-Shiftern realisiert werden.

Bei diesen Operationen werden die Ziffern nach links oder rechts verschoben. Dabei wird die Richtungsangabe unabhängig von der Rechnerarchitektur immer im arithmetischen Sinn des Dualsystems verstanden: Links bedeutet Multiplikation und rechts Division mit einer Zweierpotenz. Register der Prozessoren haben eine begrenzte Anzahl zur Verfügung stehender Speicherplätze, weshalb einige Bits an einem Ende aus dem Register „hinausgeschoben“ werden, während die gleiche Anzahl an Bits am anderen Ende „hineingeschoben“ wird. Der Unterschied zwischen den bitweisen Verschiebungsoperatoren besteht in der Berechnung der Bits, die auf diese Weise „hineingeschoben“ werden.

Beispiel

Symbolik:

  • „<<“ (in einigen Sprachen „shl“) Verschieben nach links, um den jeweils dahinter angegeben Wert
  • „>>“ (in einigen Sprachen „shr“) Verschieben nach rechts, um den jeweils dahinter angegeben Wert

In Sprachen wie C wird für Rechtsverschiebungen abhängig vom Datentyp und ggf. Vorzeichen entweder mit Nullen (unsigned oder nicht-negativ) oder mit Einsen (signed und kleiner Null) aufgefüllt. Andere Programmiersprachen (wie z.B. Java) verwenden stattdessen einen eigenen Operator >>>, bei dem stets mit Nullen aufgefüllt wird:

01001111 <<  1 = 10011110
00111100 <<  2 = 11110000
01001111 >>  1 = 00100111
11110000 >>  2 = 11111100 (signed)
11110000 >>> 2 = 00111100 (unsigned)
01001111 >>> 1 = 00100111 (signed und unsigned) 

Eine arithmetische Verschiebung um n ist äquivalent zu einer Multiplikation mit 2^{n}.

\mathrm{12_{10} = }00001100 << 2 = 00110000\mathrm{ = 48_{10} = 2^{2} \cdot 12 = 4 \cdot 12}

Dieses Verfahren stellt somit eine Alternative zur Multiplikation bzw. Division mit Zweierpotenzen dar. Divisionsergebnisse werden abgerundet. Ebenfalls ist es möglich, eine n-Bit-Zahl modulo 2k zu rechnen, indem sie um jeweils n-k nach links und wieder nach rechts verschiebt. Etwas schneller noch kann man die modulo-Berechnung über das bitweise UND mit 2k-1 durchführen.

Arithmetische Verschiebung[Bearbeiten]

Arithmetischer Linksshift
Arithmetischer Rechtsshift

Bei einer arithmetischen Verschiebung (engl. arithmetic shift) werden die hinausgeschobenen Bits abgeschnitten. Bei einer Verschiebung nach links werden auf der rechten Seite Nullen nachgeschoben; bei einer Verschiebung nach rechts werden Kopien des Vorzeichenbits auf der linken Seite eingeschoben. Beispiel (4-Bit-Register):

  0110 LINKS-SHIFT
= 1100
  1100 RECHTS-SHIFT
= 1110

Bei der Linksverschiebung wird das am weitesten links stehende Bit aus dem Register hinausgeschoben und eine neue 0 am rechten Ende eingefügt. Bei der Rechtsverschiebung wird das am weitesten rechts stehende Bit hinausgeschoben (eventuell in ein Überlaufflag) und das ursprünglich höchstwertige Bit (MSB) am linken Ende eingefügt, wodurch das Vorzeichen der Zahl erhalten bleibt. Manchmal werden mehrere Verschiebungen zu einer Verschiebung um mehrere Stellen zusammengefasst.

Eine arithmetische Verschiebung um n ist äquivalent zu einer Multiplikation mit 2^{n} (unter der Voraussetzung, dass kein Überlauf auftritt). Eine arithmetische Verschiebung einer Zweierkomplementzahl um n nach rechts entspricht einer Division durch 2^{n} und Rundung auf die nächstkleinere Zahl.

Logische Verschiebung[Bearbeiten]

Hauptartikel: Logische Verschiebung
Logischer Rechtsshift
Logischer Linksshift

Bei einer logischen Verschiebung (engl. logic shift) werden die hinausgeschobenen Bits verworfen und Nullen eingeschoben, unabhängig von Schieberichtung und Vorzeichen. Deshalb sind logische und arithmetische Verschiebung nach links identische Operationen. Bei der logischen Verschiebung nach rechts werden jedoch Nullen statt Kopien des Vorzeichenbits eingefügt. Daher wird die logische Verschiebung bei vorzeichenlosen Binärzahlen eingesetzt, während arithmetische Verschiebungen bei vorzeichenbehafteten Zweierkomplementzahlen verwendet werden.

Zyklische Verschiebung ohne Übertragsbit[Bearbeiten]

Zyklischer Rechtsshift
Zyklischer Linksshift

Eine andere Form der bitweisen Verschiebung ist die zyklische Verschiebung (engl. circular shift) oder bitweise Rotation. Bei dieser Operation „rotieren“ die Bits, als ob das linke und das rechte Ende verbunden wären. Das Bit, das hineingeschoben wird, hat denselben Wert wie das Bit, das aus dem Register hinausgeschoben wird. Diese Operation erhält alle existierenden Bits und wird teilweise in der digitalen Kryptographie eingesetzt, beispielsweise beim AES-Verfahren, von und nach seinen Entwicklern auch „Rijndael“ genannt. In elementarer Form, jedoch nicht auf Bitebene sondern auf der Basis eines Alphabets, wird sie in der Verschiebechiffre angewendet.

Zyklische Verschiebung mit Übertragsbit[Bearbeiten]

Zyklischer Rechtsshift mit Übertragsbit C (Carry)
Zyklischer Linksshift mit Übertragsbit C (Carry)

Zyklische Verschiebung mit Übertragsbit (engl. rotate through carry) funktioniert ähnlich wie die zyklische Verschiebung ohne Übertragsbit, jedoch werden die beiden Enden des Registers behandelt, als ob sie durch das Übertragsbit getrennt werden. Das Carry-Bit wird in das Register hineingeschoben, das aus dem Register hinausgeschobene Bit wird zum neuen Übertragsbit.

Eine einzelne zyklische Verschiebung mit Übertragsbit kann eine logische oder arithmetische Verschiebung um eine Stelle simulieren, wenn das Übertragsbit vorher entsprechend gesetzt wird. Enthält das Übertragsbit beispielsweise eine 0, dann entspricht die Verschiebung nach rechts einer arithmetischen Verschiebung nach rechts. Aus diesem Grund sind bei manchen Mikroprozessoren wie dem PICmicro nur Befehle für die beiden zyklischen Verschiebungsoperationen implementiert, es gibt keine speziellen Befehle für arithmetische oder logische Verschiebungen.

Zyklische Verschiebung mit Übertragsbit ist besonders nützlich, wenn Verschiebungen mit Zahlen durchgeführt werden, die größer als die Wortbreite des Prozessors sind, weil die Zahl dann in zwei Registern gespeichert wird und das aus einem Register hinausgeschobene Bit in das andere Register hineingeschoben werden muss. Bei zyklischer Verschiebung mit Übertragsbit wird dieses Bit bei der ersten Verschiebung im Übertragsbit „gespeichert“ und bei der nächsten Verschiebung weitergegeben, ohne dass zusätzliche Instruktionen notwendig sind.

Verschiebeoperatoren in Programmiersprachen[Bearbeiten]

C und C++[Bearbeiten]

In C, C++ und verwandten Sprachen werden die Verschiebungsoperatoren durch „<<“ und „>>“ dargestellt. Die Anzahl der Verschiebungen wird als zweiter Operand übergeben. Beispiel:

 x = y << 2;

weist der Variable x das Ergebnis der bitweisen Verschiebung von y um zwei Stellen nach links zu. Dies führt zum selben Ergebnis wie x = y * 4.

In C und C++ verwenden Berechnungen mit vorzeichenlosen Werten logische Verschiebungen; Berechnungen mit vorzeichenbehafteten Werten sind undefiniert, sofern der rechte Operand negativ ist, durch einen Linksshift sich das Vorzeichen ändert oder ein negativer Wert einem Rechtsshift unterzogen wird.[1]

Ebenso ist das Ergebnis laut C- und C++-Sprachnorm undefiniert, wenn die Anzahl der Bitverschiebungen größer oder gleich der Bitbreite der Rechenarchitektur ist.[2]. Wird beispielsweise auf einer 32-Bit-Architektur von Intel-Prozessoren gearbeitet (IA32), so bewirkt eine Verschiebung um 32 Stellen oft gar keine Veränderung des Ergebnisses, d.h. für x = y << 32 ergibt sich x == y. Der Grund liegt in der Art und Weise, wie die Compiler die Schiebeoperation in Maschinencode umsetzen. Die meisten Prozessoren haben direkte Befehle zum Schieben von Bits, wobei die Anzahl der Verschiebungen nur in begrenzter Breite im Maschinenbefehl codiert wird. Für IA32 sind z. B. 5 Bitstellen vorgesehen, um die Zahl der Verschiebungen abzulegen[3]. Daher können nur Verschiebungen im Bereich 0 bis 31 korrekt ausgeführt werden. Entsprechende Beschränkungen können für andere Architekturen und Datentypen ebenso vorhanden sein.

Java[Bearbeiten]

In Java sind alle Ganzzahl-Datentypen vorzeichenbehaftet, und die Operatoren „<<“ und „>>“ führen arithmetische Verschiebungen durch. In Java gibt es zusätzlich den Operator „>>>“, der eine logische Rechtsverschiebung durchführt. Da logische und arithmetische Linksverschiebungen identisch sind, gibt es keinen „<<<“-Operator.

ARM-Assembler[Bearbeiten]

In ARM-Assembler werden die Verschiebungsoperatoren durch LSL(Logical Shift Left), LSR(Logical Shift Right) und ASR(Arithmetic Shift Right) dargestellt. Für die zyklischen Verschiebungen gibt es die beiden Befehle ROR(ROtate Right, ohne Übertragsbit) und RRX(Rotate Right eXtended, mit Übertragsbit).

Anwendungen[Bearbeiten]

Obwohl Rechner oft effiziente Befehle zur Ausführung von arithmetischen und logischen Operationen eingebaut haben, können alle diese Operationen auch durch Kombinationen von bitweisen Operatoren und Nullvergleichen durchgeführt werden. Folgender Pseudocode zeigt beispielsweise, wie zwei beliebige Ganzzahlen a und b nur mithilfe von Verschiebungen und Additionen multipliziert werden können:

c := 0
solange b ≠ 0
    falls (b und 1) ≠ 0
        c := c + a
    schiebe a um 1 nach links
    schiebe b um 1 nach rechts
return c

Der Code führt eine schriftliche Multiplikation im Binärsystem aus, allerdings in der unüblichen Reihenfolge von hinten nach vorne (beginnend mit der letzten Ziffer von b).

Siehe auch: Schriftliche Multiplikation im Binärsystem

Siehe auch[Bearbeiten]

Quellen[Bearbeiten]

  1. ISO/IEC-Standard „C programming language“, Abschnitt 6.5.7#5 (englisch)
  2. A7.8 Shift Operators, Appendix A. Reference Manual, The C Programming Language
  3. SAL,SAR,SHL,SHR – Shift, Chapter 4. Instruction Set Reference, IA-32 Intel Architecture Software Developer’s Manual

Weblinks[Bearbeiten]