Bitweise Bedienung und Verwendung

102

Betrachten Sie diesen Code:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

Ich kann die arithmetischen Operatoren in Python (und anderen Sprachen) verstehen, aber ich habe 'bitweise' Operatoren nie ganz gut verstanden. Im obigen Beispiel (aus einem Python-Buch) verstehe ich die Linksverschiebung, aber nicht die beiden anderen.

Wofür werden bitweise Operatoren tatsächlich verwendet? Ich würde einige Beispiele schätzen.

eozzy
quelle
11
Dies kann von Interesse sein: stackoverflow.com/questions/276706/what-are-bitwise-operators
outis
Dies kann auch von Interesse sein: stackoverflow.com/questions/8556206/what-does-mean-in-python
Philippe Oger

Antworten:

163

Bitweise Operatoren sind Operatoren, die mit Mehrbitwerten arbeiten, jedoch konzeptionell jeweils ein Bit.

  • ANDist nur dann 1, wenn beide Eingänge 1 sind, andernfalls ist es 0.
  • ORist 1, wenn einer oder beide Eingänge 1 sind, andernfalls ist es 0.
  • XORist nur dann 1, wenn genau einer seiner Eingänge 1 ist, andernfalls ist es 0.
  • NOT ist nur dann 1, wenn seine Eingabe 0 ist, andernfalls ist es 0.

Diese können oft am besten als Wahrheitstabellen angezeigt werden. Die Eingabemöglichkeiten befinden sich oben und links. Das resultierende Bit ist einer der vier Werte (zwei im Fall von NOT, da es nur einen Eingang hat), die am Schnittpunkt der Eingänge angezeigt werden.

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

Ein Beispiel ist, wenn Sie nur die unteren 4 Bits einer ganzen Zahl wollen, Sie UND es mit 15 (binär 1111) also:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

Die Nullbits in 15 wirken in diesem Fall effektiv als Filter und zwingen die Bits im Ergebnis, ebenfalls Null zu sein.

Darüber hinaus >>und <<werden oft als Bit - Operatoren enthält, und sie „shift“ ein Wert jeweils rechts und durch eine bestimmte Anzahl von Bits nach links, Wegwerfen Bits , die Rolle des Endes in Richtung Sie Verschiebung und Fütterung in Null - Bits bei der anderes Ende.

Also zum Beispiel:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

Beachten Sie, dass die Linksverschiebung in Python insofern ungewöhnlich ist, als keine feste Breite verwendet wird, in der Bits verworfen werden. Während viele Sprachen eine feste Breite basierend auf dem Datentyp verwenden, erweitert Python die Breite einfach, um zusätzliche Bits zu berücksichtigen. Um das Verwerfungsverhalten in Python zu erhalten, können Sie eine Linksverschiebung mit einer bitweisen Verschiebung verfolgen, andz. B. bei einer 8-Bit-Wertverschiebung um vier Bit nach links:

bits8 = (bits8 << 4) & 255

In diesem Sinne ist ein weiteres Beispiel für bitweise Operatoren: Wenn Sie zwei 4-Bit-Werte haben, die Sie in einen 8-Bit-Wert packen möchten, können Sie alle drei Operatoren ( left-shift, andund or) verwenden:

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • Die & 15Operation stellt sicher, dass beide Werte nur die unteren 4 Bits haben.
  • Dies << 4ist eine 4-Bit-Verschiebung nach links, um val1in die oberen 4 Bits eines 8-Bit-Werts zu gelangen.
  • Das |kombiniert diese beiden einfach miteinander.

Wenn val1ist 7 und val2ist 4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100
paxdiablo
quelle
43

Eine typische Verwendung:

| wird verwendet, um ein bestimmtes Bit auf 1 zu setzen

& wird verwendet, um ein bestimmtes Bit zu testen oder zu löschen

  • Setzen Sie ein Bit (wobei n die Bitnummer und 0 das niedrigstwertige Bit ist):

    unsigned char a |= (1 << n);

  • Klar ein bisschen:

    unsigned char b &= ~(1 << n);

  • Ein bisschen umschalten:

    unsigned char c ^= (1 << n);

  • Teste ein bisschen:

    unsigned char e = d & (1 << n);

Nehmen Sie zum Beispiel den Fall Ihrer Liste:

x | 2wird verwendet, um Bit 1 von xauf 1 zu setzen

x & 1wird verwendet, um zu testen, ob Bit 0 von x1 oder 0 ist

pierrotlefou
quelle
38

Wofür werden bitweise Operatoren tatsächlich verwendet? Ich würde einige Beispiele schätzen.

Eine der häufigsten Anwendungen von bitweisen Operationen ist das Parsen von hexadezimalen Farben.

Hier ist beispielsweise eine Python- Funktion, die einen String wie akzeptiert #FF09BEund ein Tupel ihrer Werte für Rot, Grün und Blau zurückgibt.

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

Ich weiß, dass es effizientere Möglichkeiten gibt, dies zu erreichen, aber ich glaube, dass dies ein wirklich prägnantes Beispiel ist, das sowohl Verschiebungen als auch bitweise boolesche Operationen veranschaulicht.

bguiz
quelle
14

Ich denke, dass der zweite Teil der Frage:

Wofür werden bitweise Operatoren tatsächlich verwendet? Ich würde einige Beispiele schätzen.

Wurde nur teilweise angesprochen. Das sind meine zwei Cent in dieser Angelegenheit.

Bitweise Operationen in Programmiersprachen spielen bei vielen Anwendungen eine grundlegende Rolle. Fast alle Low-Level-Berechnungen müssen mit dieser Art von Operationen durchgeführt werden.

In allen Anwendungen, die Daten zwischen zwei Knoten senden müssen, z.

  • Computernetzwerke;

  • Telekommunikationsanwendungen (Mobiltelefone, Satellitenkommunikation usw.).

In der unteren Kommunikationsebene werden die Daten normalerweise in sogenannten Frames gesendet . Frames sind nur Zeichenfolgen von Bytes, die über einen physischen Kanal gesendet werden. Diese Frames enthalten normalerweise die tatsächlichen Daten sowie einige andere Felder (in Bytes codiert), die Teil des sogenannten Headers sind . Der Header enthält normalerweise Bytes, die einige Informationen codieren, die sich auf den Status der Kommunikation beziehen (z. B. mit Flags (Bits)), Rahmenzählern, Korrektur- und Fehlererkennungscodes usw., um die übertragenen Daten in einem Rahmen abzurufen und die zu erstellen Frames, um Daten zu senden, benötigen Sie sicher bitweise Operationen.

Wenn Sie mit solchen Anwendungen arbeiten, steht im Allgemeinen eine API zur Verfügung, sodass Sie sich nicht mit all diesen Details befassen müssen. Beispielsweise bieten alle modernen Programmiersprachen Bibliotheken für Socket-Verbindungen, sodass Sie die TCP / IP-Kommunikationsrahmen nicht erstellen müssen. Aber denken Sie an die guten Leute, die diese APIs für Sie programmiert haben. Sie mussten sich mit Sicherheit mit der Rahmenkonstruktion befassen. Verwenden aller Arten von bitweisen Operationen, um von der Kommunikation auf niedriger Ebene zur Kommunikation auf höherer Ebene hin und her zu gelangen.

Stellen Sie sich als konkretes Beispiel vor, jemand gibt Ihnen eine Datei mit Rohdaten, die direkt von Telekommunikationshardware erfasst wurden. In diesem Fall müssen Sie zum Auffinden der Frames die Rohbytes in der Datei lesen und versuchen, Synchronisationswörter zu finden, indem Sie die Daten Stück für Stück scannen. Nachdem Sie die Synchronisationswörter identifiziert haben, müssen Sie die tatsächlichen Frames abrufen und sie bei Bedarf verschieben (und dies ist nur der Anfang der Geschichte), um die tatsächlichen Daten zu erhalten, die übertragen werden.

Eine andere, sehr unterschiedliche Anwendungsfamilie auf niedriger Ebene besteht darin, dass Sie die Hardware über einige (alte) Ports steuern müssen, z. B. parallele und serielle Ports. Diese Ports werden durch Setzen einiger Bytes gesteuert, und jedes Bit dieser Bytes hat in Bezug auf Anweisungen eine bestimmte Bedeutung für diesen Port (siehe zum Beispiel http://en.wikipedia.org/wiki/Parallel_port ). Wenn Sie Software erstellen möchten, die etwas mit dieser Hardware tut, benötigen Sie bitweise Operationen, um die Anweisungen, die Sie ausführen möchten, in die Bytes zu übersetzen, die der Port versteht.

Wenn Sie beispielsweise einige physische Tasten an den parallelen Anschluss angeschlossen haben, um ein anderes Gerät zu steuern, ist dies eine Codezeile, die Sie in der Soft-Anwendung finden:

read = ((read ^ 0x80) >> 4) & 0x0f; 

Hoffe das trägt dazu bei.

eguaio
quelle
Ich würde en.wikipedia.org/wiki/Bit_banging als eine weitere Möglichkeit hinzufügen, die es zu erkunden gilt, insbesondere wenn Sie als Beispiel über parallele und serielle Schnittstellen lesen, bei denen bitweise Operationen nützlich sein können.
Dan
6

Ich hoffe, das klärt diese beiden:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1
Amarghosh
quelle
4
Ups ... versuchte die schnellste Waffe im Westen zu sein ... endete als Idiot, der nicht einmal Binär für zwei kennt :(
Behoben
1
x & 1veranschaulicht den Effekt nicht so gut wie x & 2würde.
Dansalmo
5

Stellen Sie sich 0 als falsch und 1 als wahr vor. Dann funktionieren bitweise und (&) und oder (|) genau wie regulär und und oder, außer dass sie alle Bits im Wert gleichzeitig ausführen. In der Regel werden sie für Flags verwendet, wenn Sie über 30 Optionen verfügen, die festgelegt werden können (z. B. als Zeichenstile in einem Fenster). Sie möchten nicht 30 separate boolesche Werte übergeben müssen, um die einzelnen Werte festzulegen oder zu deaktivieren, sodass Sie | verwenden um Optionen zu einem einzigen Wert zu kombinieren und dann mit & zu prüfen, ob jede Option gesetzt ist. Diese Art der Flaggenübergabe wird von OpenGL häufig verwendet. Da jedes Bit ein separates Flag ist, erhalten Sie Flag-Werte für Zweierpotenzen (auch bekannt als Zahlen, bei denen nur ein Bit gesetzt ist). 1 (2 ^ 0) 2 (2 ^ 1) 4 (2 ^ 2) 8 (2 ^ 3) the Die Zweierpotenz gibt an, welches Bit gesetzt ist, wenn das Flag aktiviert ist.

Beachten Sie auch 2 = 10, so dass x | 2 110 (6) und nicht 111 (7) ist, wenn sich keines der Bits überlappt (was in diesem Fall zutrifft) | wirkt wie Addition.

Steinmetall
quelle
5

Ich habe es oben nicht erwähnt, aber Sie werden auch sehen, dass einige Leute die Links- und Rechtsverschiebung für arithmetische Operationen verwenden. Eine Verschiebung nach links mit x entspricht dem Multiplizieren mit 2 ^ x (solange es nicht überläuft) und eine Verschiebung nach rechts entspricht dem Teilen durch 2 ^ x.

Kürzlich habe ich Leute gesehen, die x << 1 und x >> 1 zum Verdoppeln und Halbieren verwenden, obwohl ich nicht sicher bin, ob sie nur versuchen, klug zu sein, oder ob es wirklich einen deutlichen Vorteil gegenüber den normalen Operatoren gibt.

P. Stallworth
quelle
1
Ich weiß nichts über Python, aber in Sprachen niedrigerer Ebenen wie C oder sogar niedriger - Assembly ist die bitweise Verschiebung viel effizienter. Um den Unterschied zu erkennen, können Sie ein Programm in C schreiben, das dies auf jede Weise tut, und einfach zu Assembler-Code kompilieren (oder wenn Sie Assembly Lang kennen, würden Sie dies bereits wissen :)). Sehen Sie den Unterschied in der Anzahl der Anweisungen.
0xc0de
2
Mein Argument gegen die Verwendung der Bitverschiebungsoperatoren wäre, dass die meisten modernen Compiler wahrscheinlich bereits arithmetische Operationen optimieren, damit die Klugheit bestenfalls umstritten ist oder im schlimmsten Fall gegen den Compiler kämpft. Ich habe keine Erfahrung mit C, Compilern oder CPU-Designs und gehe daher nicht davon aus, dass ich richtig bin. :)
P. Stallworth
Dies sollte höher sein. Ich musste mich mit einem Code befassen, der genau so den bitweisen Operator verwendete, und diese Antwort half mir, die Dinge herauszufinden.
Philippe Oger
4

Sets

Mengen können mit mathematischen Operationen kombiniert werden.

  • Der Union-Operator |kombiniert zwei Sätze zu einem neuen, der Elemente enthält.
  • Der Kreuzungsoperator &erhält nur Elemente in beiden.
  • Der Differenzoperator -erhält Elemente im ersten Satz, jedoch nicht im zweiten.
  • Der symmetrische Differenzoperator ^erhält Elemente in beiden Sätzen, jedoch nicht in beiden.

Versuch es selber:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

Ergebnis:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}
PUNEET JAIN
quelle
Diese Antwort hat nichts mit der Frage zu tun und scheint von einer anderen Stelle kopiert und eingefügt worden zu sein.
Doctaphred
Die Frage lautet "Wofür werden bitweise Operatoren tatsächlich verwendet?". Diese Antwort bietet eine weniger bekannte, aber sehr nützliche Verwendung der bitweisen Operatoren.
Taegyung
3

Dieses Beispiel zeigt Ihnen die Operationen für alle vier 2-Bit-Werte:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

Hier ist ein Anwendungsbeispiel:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]
Dansalmo
quelle
2

Ein weiterer häufiger Anwendungsfall ist das Bearbeiten / Testen von Dateiberechtigungen. Siehe das Python-Statistikmodul: http://docs.python.org/library/stat.html .

Um beispielsweise die Berechtigungen einer Datei mit einem gewünschten Berechtigungssatz zu vergleichen, können Sie Folgendes tun:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

Ich habe die Ergebnisse als Boolesche Werte gewertet, weil mir nur die Wahrheit oder Falschheit wichtig ist, aber es wäre eine lohnende Übung, die bin () -Werte für jeden einzelnen auszudrucken.

dbn
quelle
1
Sie liegen im letzten Beispiel falsch. So sollte es aussehen : not bool((mode ^ desired_mode) & 0777). Oder (leichter zu verstehen) : not (mode & 0777) ^ desired_mode == 0. UND hinterlässt nur interessante Bits, XOR prüft, ob alle gewünschten Bits gesetzt sind. Expliziter == 0Vergleich ist sinnvoller als bool().
Vadim Fint
Ich denke nicht, dass dies spezifisch für Dateivorgänge ist. Zum Beispiel machen Sie in PyQt etwas Ähnliches für setWindowFlags. Beispiel : setWindowFlags(SplashScreen | WindowStaysOnTopHint). Ich finde das immer noch verwirrend, da es wie ein Umschalter erscheint, den Sie auf "Ein" setzen, so dass es in einem solchen Fall intuitiver zu "und" erscheint.
Eric
2

Bitdarstellungen von Ganzzahlen werden im wissenschaftlichen Rechnen häufig verwendet, um Arrays von True-False-Informationen darzustellen, da eine bitweise Operation viel schneller ist als das Durchlaufen eines Arrays von Booleschen Werten. (Höhere Sprachen verwenden möglicherweise die Idee eines Bit-Arrays.)

Ein schönes und ziemlich einfaches Beispiel dafür ist die allgemeine Lösung für das Spiel Nim. Schauen Sie sich den Python- Code auf der Wikipedia-Seite an . Es nutzt stark bitweise exklusiv oder , ^.

Jona
quelle
1

Es gibt möglicherweise einen besseren Weg, um herauszufinden, wo sich ein Array-Element zwischen zwei Werten befindet. Wie dieses Beispiel zeigt, funktioniert das & hier, wohingegen und nicht.

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)
Holzfuß
quelle
1

Ich habe es nicht erwähnt gesehen. Dieses Beispiel zeigt Ihnen die (-) Dezimaloperation für 2-Bit-Werte: AB (nur wenn A B enthält)

Diese Operation wird benötigt, wenn wir in unserem Programm ein Verb halten, das Bits darstellt. manchmal müssen wir Bits hinzufügen (wie oben) und manchmal müssen wir Bits entfernen (wenn das Verb dann enthält)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

mit Python: 7 & ~ 4 = 3 (entferne von 7 die Bits, die 4 darstellen)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

mit Python: 1 & ~ 4 = 1 (entferne von 1 die Bits, die 4 darstellen - in diesem Fall ist 1 nicht 'enthält' 4).

Eyal Ch
quelle
0

Während das Manipulieren von Bits einer Ganzzahl nützlich ist, kann es häufig für Netzwerkprotokolle, die bis auf das Bit spezifiziert werden können, das Manipulieren längerer Byte-Sequenzen erfordern (die nicht einfach in eine Ganzzahl konvertiert werden können). In diesem Fall ist es nützlich, die Bitstring- Bibliothek zu verwenden, die bitweise Operationen an Daten ermöglicht - z. B. kann man die Zeichenfolge 'ABCDEFGHIJKLMNOPQ' als Zeichenfolge oder als Hex- und Bitverschiebung importieren (oder andere bitweise Operationen ausführen):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
Pierz
quelle
0

die folgenden bitweisen Operatoren: & , | , ^ und ~ geben Werte (basierend auf ihrer Eingabe) auf die gleiche Weise zurück, wie Logikgatter Signale beeinflussen. Sie können sie verwenden, um Schaltkreise zu emulieren.

Erik Waters
quelle
0

Um Bits umzudrehen (dh das Komplement / Invertieren von 1), können Sie Folgendes tun:

Da der Wert ExORed mit allen Einsen zur Inversion führt, können Sie sie für eine bestimmte Bitbreite mit ExOR invertieren.

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
user11034079
quelle