Tracks
/
Python
Python
/
Lehrplan
/
Bitweise Operatoren
Bi

Bitweise Operatoren in Python

{one: "1 Übung", other: "%{count} Übungen"}

Über Bitweise Operatoren

Ganz unten auf der Hardware-Ebene kennen Transistoren nur zwei Zustände: an oder aus. Diese beiden Zustände stellen wir traditionell mit 1 und 0 dar. Das sind die binary digits, abgekürzt bits. Ein Bewusstsein für bits und binary ist besonders wichtig für Systemprogrammierer, die in Low-Level-Sprachen arbeiten.

Doch über den größten Teil der Computergeschichte war das vorrangige Ziel der Programmierung, diese binäre Realität mit immer ausgefeilteren Mitteln zu abstrahieren. In Python (und vielen anderen High-Level-Programmiersprachen) arbeiten wir mit int, float, string und anderen definierten Typen, bis hin zu Audio- und Videoformaten. Die Interna von Python kümmern sich darum, (irgendwann) alles in Bits zu übersetzen.

Dennoch können Bit-Operatoren und Bit-Operationen manchmal deutliche Vorteile bei Geschwindigkeit und Speichereffizienz bieten, selbst in einer High-Level-Sprache wie Python.

Binärzahlen eingeben und anzeigen

Wie nicht anders zu erwarten, verwendet Python im Umgang mit dem Nutzer Dezimalzahlen, doch diese Voreinstellung lässt sich überschreiben. Tatsächlich akzeptiert Python ohne Weiteres einen int im binary-, hexadecimal- oder octal-Format und führt auch bereitwillig mathematische Operationen zwischen ihnen aus. Für mehr Details kannst du dir das Konzept binary-octal-hexadecimal ansehen.

Binärzahlen werden mit dem Präfix 0b eingegeben, so wie 0x für Hexadezimalzahlen verwendet werden kann (Hexadezimalzahlen sind eine kompakte Art, Gruppen von 4 Bits darzustellen) und oct für Oktalzahlen.

Es gibt mehrere Möglichkeiten, Ganzzahlen in Binär-Strings umzuwandeln. Sie unterscheiden sich darin, ob sie das Präfix 0b enthalten und ob sie linksseitiges Auffüllen mit Nullen unterstützen:

# Binary entry.
>>> 0b10111
23

# Converting an int display to binary string, with prefix.
>>> bin(23)  
'0b10111'

>>> number = 23

# Binary without prefix, padded to 8 digits.
>>> format(number, '08b')  
'00010111'

# Same format, but using an f-string.
>>> f"{number} in decimal is {number:08b} in binary and {number:x} in hex" 
'23 in decimal is 00010111 in binary and 17 in hex'

Bitwise Logic

Im Konzept bools haben wir die logischen Operatoren and, or und not besprochen, die mit booleschen Werten (True und False) verwendet werden. Beim Arbeiten mit Bits gelten dieselben logischen Regeln.

Die bitweisen Entsprechungen der logischen Operatoren & (and), | (or), ~ (not) und ^ (XOR) werden jedoch auf jedes einzelne Bit einer binären Darstellung angewendet, wobei 1 als True („an“) und 0 als False („aus“) gilt. Ein Beispiel mit dem bitweisen & macht das vielleicht klarer:

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x & y, '08b')
'00100010'

Nur Positionen, an denen in beiden Eingabezahlen eine 1 steht, werden in der Ausgabe auf 1 gesetzt.

Das bitweise & wird häufig verwendet, um einzelne Bits in einer kompakten Menge von True/False-Werten zu isolieren, etwa bei benutzerdefinierbaren Einstellungen in einer App. So kann der Wert einzelner Bits die Programmlogik steuern:

>>> number = 0b0110
>>> number & 0b0001 > 0
False

>>> number & 0b0010 > 0
True

Beim bitweisen | (or) wird in die Ausgabe eine 1 gesetzt, wenn in einer der beiden Eingaben eine 1 steht:

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x | y, '08b')
'01101110'

Beim Operator ^ für das bitweise exklusive Oder (XOR) wird eine 1 gesetzt, wenn sie in einer der Eingaben vorkommt, aber nicht in beiden. Dieses Symbol kommt dir vielleicht aus dem Konzept sets bekannt vor, wo es für die symmetrische Differenz von set steht. Das ist dasselbe wie XOR angewendet auf Mengen. Wenn dir XOR ^ seltsam vorkommt, sei dir bewusst, dass es mit Abstand die häufigste Operation in der Kryptografie ist.

>>> x = 0b01100110
>>> y = 0b00101010

>>> format(x ^ y, '08b')
'01001100'

Schließlich gibt es den Operator ~ (das Zeichen Tilde), ein bitweises not, das eine einzelne Eingabe nimmt und alle Bits invertiert. Das ist vielleicht nicht das Ergebnis, das du erwartet hast! Jede 1 in der Darstellung wird zu 0 und umgekehrt. Details findest du im folgenden Abschnitt.

Negative Zahlen und ihre binäre Darstellung

In der Dezimaldarstellung unterscheiden wir positive und negative Zahlen dadurch, dass wir links von den Ziffern ein +- oder --Zeichen setzen. Auf binärer Ebene erwies sich die Verwendung dieser Symbole für die digitale Datenverarbeitung als ineffizient und warf das Problem auf, dass +0 nicht dasselbe ist wie -0.

Statt - und + zu verwenden, nutzen alle modernen Computer eine two's complement-Darstellung für negative Zahlen, bis hinunter auf die Ebene des Siliziumchips. Das bedeutet: Alle Bits werden invertiert, und eine Zahl wird als negativ interpretiert, wenn das linkeste Bit (auch „höchstwertiges Bit“ oder MSB genannt) eine 1 ist. Positive Zahlen haben ein MSB von 0. Diese Darstellung hat den Vorteil, dass es nur eine Version der Null gibt, sodass du dich nicht um -0 und +0 kümmern musst.

Diese Art, negative und positive Zahlen darzustellen, bringt für Python eine Komplikation mit sich: In der Kernsprache gibt es intern keine Konzepte für Ganzzahlen fester Größe wie int32 oder int64. In „modernem“ Python haben ints unbegrenzte Größe (nur begrenzt durch die Hardware), und eine negative oder bit-invertierte Zahl hat links (theoretisch) unendlich viele 1, so wie eine positive Zahl unbegrenzt viele 0 hat.

Dadurch ist es schwierig, ein sinnvolles Beispiel für das bitweise bitwise not zu zeigen:

>>> x = 0b01100110
>>> format(x, '08b')
'01100110'

# This is a negative binary (not twos-complement display).
>>> format(~x, '08b')
'-1100111'  

 # Decimal representation.
>>> x
102

# Using the Bitwise not, with an unintuitive result.
>>> ~x
-103

Das ist nicht die 0b10011001, die wir in Sprachen mit Ganzzahlen fester Größe sehen würden.

Der Operator ~ funktioniert nur bei vorzeichenlosen Byte- oder Ganzzahltypen oder bei Ganzzahltypen fester Größe so, wie man es erwartet. Diese numerischen Typen werden in Paketen von Drittanbietern wie NumPy, pandas und sympy unterstützt, aber nicht im Kern von Python.

In der Praxis verwenden Python-Programmierer &, |, ^ und die unten beschriebenen Shift-Operatoren recht häufig nur mit positiven Zahlen. Bitweise Operationen mit negativen Zahlen sind viel seltener. Eine Technik besteht darin, 2**32 (or 1 << 32) zu einem negativen Wert zu addieren, um den int vorzeichenlos zu machen, aber das wird schnell unhandlich. Eine andere Strategie ist die Arbeit mit dem Modul ctypes und mit C-artigen Ganzzahltypen, aber das ist genauso sperrig.

Shift operators

Der Links-Shift-Operator x << y verschiebt alle Bits in x um y Stellen nach links und füllt die neuen Lücken mit Nullen. Beachte, dass das arithmetisch dasselbe ist wie eine Multiplikation der Zahl mit (2**y).

Der Rechts-Shift-Operator x >> y macht das Gegenteil. Das ist arithmetisch dasselbe wie die Ganzzahldivision x // (2**y).

Denk an den vorherigen Abschnitt über negative Zahlen und die Fallstricke, wenn du sie in Python verschiebst.

>>> x = 8
>>> format(x, '08b')
'00001000'

# A left bit shift. 
>>> x << 2  
32

>>> format(x << 2, '08b')
'00100000'

# A right bit shift. 
>>> format(x >> 2, '08b')
'00000010'
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab