Was sind bitweise Verschiebungsoperatoren (Bitverschiebungsoperatoren) und wie funktionieren sie?

1382

Ich habe versucht, C in meiner Freizeit zu lernen, und andere Sprachen (C #, Java usw.) haben das gleiche Konzept (und oft die gleichen Operatoren) ...

Was ich frage mich ist, bei einer Kern Ebene, was bedeutet Bit-Verschiebung ( <<, >>, >>>) zu tun, welche Probleme kann es lösen helfen, und was gotchas lauert die Biegung herum? Mit anderen Worten, ein absoluter Anfängerleitfaden für Bit Shifting in all seiner Güte.

John Rudy
quelle
2
Es gibt nur wenige funktionale oder nicht funktionale Fälle, in denen Sie Bitshifting in 3GLs verwenden würden.
Troy DeMonbreun
15
Nachdem diese Antworten lesen können Sie auf diese Links aussehen wollen: graphics.stanford.edu/~seander/bithacks.html & jjj.de/bitwizardry/bitwizardrypage.html
Klauen
1
Es ist wichtig zu beachten, dass das Verschieben von Bits für Computer extrem einfach und schnell ist. Indem Sie Möglichkeiten finden, die Bitverschiebung in Ihrem Programm zu verwenden, können Sie die Speichernutzung und die Ausführungszeiten erheblich reduzieren.
Hoytman
@Hoytman: Beachten Sie jedoch, dass gute Compiler viele dieser Tricks bereits kennen und in der Regel besser erkennen können, wo dies sinnvoll ist.
Sebastian Mach

Antworten:

1713

Die Bitverschiebungsoperatoren tun genau das, was ihr Name impliziert. Sie verschieben Bits. Hier ist eine kurze (oder nicht so kurze) Einführung in die verschiedenen Schichtbetreiber.

Die Betreiber

  • >> ist der arithmetische (oder vorzeichenbehaftete) Rechtsverschiebungsoperator.
  • >>> ist der logische (oder vorzeichenlose) Rechtsschichtoperator.
  • << ist der Linksverschiebungsoperator und erfüllt die Anforderungen sowohl logischer als auch arithmetischer Verschiebungen.

Alle diese Operatoren können auf ganzzahlige Werte angewendet werden ( int, long, gegebenenfalls shortund byteoder char). In einigen Sprachen wird intder Operand durch Anwenden der Verschiebungsoperatoren auf einen beliebigen Datentyp, der kleiner als automatisch ist, automatisch geändert int.

Beachten Sie, dass dies <<<kein Operator ist, da er redundant wäre.

Beachten Sie auch, dass C und C ++ nicht zwischen den rechten Schichtoperatoren unterscheiden . Sie stellen nur den >>Operator bereit , und das Rechtsverschiebungsverhalten ist die für signierte Typen definierte Implementierung. Der Rest der Antwort verwendet die C # / Java-Operatoren.

(In allen gängigen C- und C ++ - Implementierungen, einschließlich GCC und Clang / LLVM, sind >>signierte Typen arithmetisch. Einige Codes setzen dies voraus, dies wird jedoch vom Standard nicht garantiert. Es ist jedoch nicht undefiniert . Der Standard erfordert Implementierungen, um ihn zu definieren So oder so. Linksverschiebungen von negativ vorzeichenbehafteten Zahlen sind jedoch undefiniertes Verhalten (vorzeichenbehafteter Ganzzahlüberlauf). Wenn Sie also keine arithmetische Rechtsverschiebung benötigen, ist es normalerweise eine gute Idee, Ihre Bitverschiebung mit vorzeichenlosen Typen durchzuführen.)


Linksverschiebung (<<)

Ganzzahlen werden im Speicher als eine Reihe von Bits gespeichert. Die als 32-Bit gespeicherte Nummer 6 intwäre beispielsweise:

00000000 00000000 00000000 00000110

Wenn Sie dieses Bitmuster um eine Position ( 6 << 1) nach links verschieben, erhalten Sie die Nummer 12:

00000000 00000000 00000000 00001100

Wie Sie sehen können, haben sich die Ziffern um eine Position nach links verschoben, und die letzte Ziffer rechts ist mit einer Null gefüllt. Sie können auch feststellen, dass das Verschieben nach links einer Multiplikation mit Potenzen von 2 6 << 1entspricht . Dies entspricht also 6 * 2und 6 << 3entspricht 6 * 8. Ein guter optimierender Compiler ersetzt Multiplikationen nach Möglichkeit durch Verschiebungen.

Nicht kreisförmige Verschiebung

Bitte beachten Sie, dass dies keine Kreisverschiebungen sind. Verschieben Sie diesen Wert um eine Position nach links ( 3,758,096,384 << 1):

11100000 00000000 00000000 00000000

Ergebnisse in 3.221.225.472:

11000000 00000000 00000000 00000000

Die Ziffer, die "vom Ende" verschoben wird, geht verloren. Es wickelt sich nicht herum.


Logische Rechtsverschiebung (>>>)

Eine logische Rechtsverschiebung ist die Umkehrung zur Linksverschiebung. Anstatt Bits nach links zu verschieben, bewegen sie sich einfach nach rechts. Zum Beispiel die Nummer 12 verschieben:

00000000 00000000 00000000 00001100

rechts um eine Position ( 12 >>> 1) erhalten wir unsere ursprüngliche 6 zurück:

00000000 00000000 00000000 00000110

Wir sehen also, dass eine Verschiebung nach rechts einer Division durch Potenzen von 2 entspricht.

Verlorene Teile sind weg

Eine Verschiebung kann jedoch keine "verlorenen" Bits zurückfordern. Wenn wir zum Beispiel dieses Muster verschieben:

00111000 00000000 00000000 00000110

links 4 Positionen ( 939,524,102 << 4) erhalten wir 2.147.483.744:

10000000 00000000 00000000 01100000

und dann zurückschalten ( (939,524,102 << 4) >>> 4) erhalten wir 134.217.734:

00001000 00000000 00000000 00000110

Wir können unseren ursprünglichen Wert nicht zurückerhalten, wenn wir Bits verloren haben.


Arithmetische Rechtsverschiebung (>>)

Die arithmetische Rechtsverschiebung entspricht genau der logischen Rechtsverschiebung, außer dass anstelle des Auffüllens mit Null das höchstwertige Bit aufgefüllt wird. Dies liegt daran, dass das höchstwertige Bit das Vorzeichenbit oder das Bit ist, das positive und negative Zahlen unterscheidet. Durch Auffüllen mit dem höchstwertigen Bit bleibt die arithmetische Rechtsverschiebung vorzeichenerhaltend.

Wenn wir dieses Bitmuster beispielsweise als negative Zahl interpretieren:

10000000 00000000 00000000 01100000

Wir haben die Nummer -2.147.483.552. Wenn wir dies mit der arithmetischen Verschiebung (-2.147.483.552 >> 4) um 4 Positionen nach rechts verschieben, erhalten wir:

11111000 00000000 00000000 00000110

oder die Nummer -134,217,722.

Wir sehen also, dass wir das Vorzeichen unserer negativen Zahlen beibehalten haben, indem wir die arithmetische Rechtsverschiebung anstelle der logischen Rechtsverschiebung verwenden. Und wieder sehen wir, dass wir eine Division durch Potenzen von 2 durchführen.

Derek Park
quelle
304
Die Antwort sollte klarer machen, dass dies eine Java-spezifische Antwort ist. Es gibt keinen >>> Operator in C / C ++ oder C #, und ob >> das Vorzeichen verbreitet oder nicht, ist die in C / C ++ definierte Implementierung (ein wichtiges potenzielles Problem)
Michael Burr
56
Die Antwort ist im Kontext der C-Sprache völlig falsch. Es gibt keine sinnvolle Unterteilung in "arithmetische" und "logische" Verschiebungen in C. In C funktionieren die Verschiebungen erwartungsgemäß bei vorzeichenlosen Werten und bei positiv vorzeichenbehafteten Werten - sie verschieben nur Bits. Bei negativen Werten ist die Rechtsverschiebung durch die Implementierung definiert (dh es kann nichts darüber gesagt werden, was sie im Allgemeinen tut), und die Linksverschiebung ist einfach verboten - sie erzeugt undefiniertes Verhalten.
Am
10
Audrey, es gibt sicherlich einen Unterschied zwischen arithmetischer und logischer Rechtsverschiebung. C lässt einfach die Auswahlimplementierung definiert. Und eine Linksverschiebung bei negativen Werten ist definitiv nicht verboten. Wenn Sie 0xff000000 um ein Bit nach links verschieben, erhalten Sie 0xfe000000.
Derek Park
16
A good optimizing compiler will substitute shifts for multiplications when possible. Was? Bitshifts sind um Größenordnungen schneller, wenn es um die Low-Level-Operationen einer CPU geht. Ein guter optimierender Compiler würde genau das Gegenteil tun , dh gewöhnliche Multiplikationen mit Zweierpotenzen in Bitverschiebungen umwandeln.
Mahn
55
@ Mahn, du liest es rückwärts von meiner Absicht. X durch Y ersetzen bedeutet, X durch Y zu ersetzen. Y ist der Ersatz für X. Die Verschiebung ist also der Ersatz für die Multiplikation.
Derek Park
209

Nehmen wir an, wir haben ein einzelnes Byte:

0110110

Das Anwenden einer einzelnen linken Bitverschiebung bringt uns:

1101100

Die Null ganz links wurde aus dem Byte heraus verschoben, und eine neue Null wurde an das rechte Ende des Bytes angehängt.

Die Bits rollen nicht über; Sie werden verworfen. Das heißt, wenn Sie die Schicht 1101100 nach links und dann nach rechts verschieben, erhalten Sie nicht das gleiche Ergebnis zurück.

Schalten durch N links äquivalent von 2 bis Multiplizieren N .

Das Verschieben nach rechts um N ist (wenn Sie das Einsenkomplement verwenden ) das Äquivalent zum Teilen durch 2 N und Runden auf Null.

Bitshifting kann für wahnsinnig schnelle Multiplikation und Division verwendet werden, vorausgesetzt, Sie arbeiten mit einer Potenz von 2. Fast alle Low-Level-Grafikroutinen verwenden Bitshifting.

Zum Beispiel haben wir früher den Modus 13h (320x200 256 Farben) für Spiele verwendet. Im Modus 13h wurde der Videospeicher nacheinander pro Pixel angeordnet. Um den Ort für ein Pixel zu berechnen, würden Sie die folgende Mathematik verwenden:

memoryOffset = (row * 320) + column

Damals war die Geschwindigkeit entscheidend, daher verwendeten wir Bit-Shifts, um diesen Vorgang durchzuführen.

320 ist jedoch keine Zweierpotenz. Um dies zu umgehen, müssen wir herausfinden, was eine Zweierpotenz ist, die zusammen 320 ergibt:

(row * 320) = (row * 256) + (row * 64)

Jetzt können wir das in Linksverschiebungen umwandeln:

(row * 320) = (row << 8) + (row << 6)

Für ein Endergebnis von:

memoryOffset = ((row << 8) + (row << 6)) + column

Jetzt erhalten wir den gleichen Versatz wie zuvor, außer dass wir anstelle einer teuren Multiplikationsoperation die beiden Bitverschiebungen verwenden ... in x86 wäre es ungefähr so ​​(Hinweis, es ist für immer her, seit ich die Montage durchgeführt habe (Anmerkung des Herausgebers: korrigiert) ein paar Fehler und ein 32-Bit-Beispiel hinzugefügt)):

mov ax, 320; 2 cycles
mul word [row]; 22 CPU Cycles
mov di,ax; 2 cycles
add di, [column]; 2 cycles
; di = [row]*320 + [column]

; 16-bit addressing mode limitations:
; [di] is a valid addressing mode, but [ax] isn't, otherwise we could skip the last mov

Insgesamt: 28 Zyklen auf einer alten CPU mit diesen Timings.

Vrs

mov ax, [row]; 2 cycles
mov di, ax; 2
shl ax, 6;  2
shl di, 8;  2
add di, ax; 2    (320 = 256+64)
add di, [column]; 2
; di = [row]*(256+64) + [column]

12 Zyklen auf derselben alten CPU.

Ja, wir würden so hart arbeiten, um 16 CPU-Zyklen zu sparen.

Im 32- oder 64-Bit-Modus werden beide Versionen viel kürzer und schneller. Moderne Out-of-Order-Ausführungs-CPUs wie Intel Skylake (siehe http://agner.org/optimize/ ) weisen eine sehr schnelle Hardware-Multiplikation auf (geringe Latenz und hoher Durchsatz), sodass die Verstärkung viel geringer ist. Die AMD Bulldozer-Familie ist etwas langsamer, insbesondere für die 64-Bit-Multiplikation. Auf Intel-CPUs und AMD Ryzen bedeuten zwei Schichten eine etwas geringere Latenz, aber mehr Anweisungen als eine Multiplikation (was zu einem geringeren Durchsatz führen kann):

imul edi, [row], 320    ; 3 cycle latency from [row] being ready
add  edi, [column]      ; 1 cycle latency (from [column] and edi being ready).
; edi = [row]*(256+64) + [column],  in 4 cycles from [row] being ready.

vs.

mov edi, [row]
shl edi, 6               ; row*64.   1 cycle latency
lea edi, [edi + edi*4]   ; row*(64 + 64*4).  1 cycle latency
add edi, [column]        ; 1 cycle latency from edi and [column] both being ready
; edi = [row]*(256+64) + [column],  in 3 cycles from [row] being ready.

Compiler erledigen dies für Sie: Sehen Sie, wie GCC, Clang und Microsoft Visual C ++ bei der Optimierung alle Shift + Lea verwendenreturn 320*row + col; .

Das Interessanteste dabei ist, dass x86 über eine Shift-and-Add-Anweisung ( LEA) verfügt , die kleine Linksverschiebungen ausführen und gleichzeitig hinzufügen kann, wobei die Leistung eine addAnweisung ist. ARM ist noch leistungsfähiger: Ein Operand einer Anweisung kann kostenlos nach links oder rechts verschoben werden. Die Skalierung mit einer Kompilierungszeitkonstante, die als Zweierpotenz bekannt ist, kann also noch effizienter sein als eine Multiplikation.


OK, früher ... etwas Nützlicheres wäre jetzt, Bitshifting zu verwenden, um zwei 8-Bit-Werte in einer 16-Bit-Ganzzahl zu speichern. Zum Beispiel in C #:

// Byte1: 11110000
// Byte2: 00001111

Int16 value = ((byte)(Byte1 >> 8) | Byte2));

// value = 000011111110000;

In C ++ sollten Compiler dies für Sie tun, wenn Sie a structmit zwei 8-Bit-Mitgliedern verwendet haben, in der Praxis jedoch nicht immer.

FlySwat
quelle
8
Wenn Sie dies erweitern, ist dies auf Intel-Prozessoren (und vielen anderen) schneller möglich: int c, d; c = d << 2; Dann: c = 4 * d; Manchmal ist sogar "c = d << 2 + d << 1" schneller als "c = 6 * d" !! Ich habe diese Tricks in der DOS-Ära ausgiebig für
Joe Pineda
5
@James: Nicht ganz, heutzutage ist es eher die Firmware der Grafikkarte, die solchen Code enthält, der von der GPU und nicht von der CPU ausgeführt wird. Theoretisch müssen Sie also keinen Code wie diesen (oder wie Carmacks Black-Magic-Inverse-Root-Funktion) für Grafikfunktionen implementieren :-)
Joe Pineda
3
@JoePineda @james Die Compiler-Autoren verwenden sie definitiv. Wenn Sie schreiben, erhalten c=4*dSie eine Schicht. Wenn Sie schreiben k = (n<0), kann dies auch mit Schichten geschehen: k = (n>>31)&1um eine Verzweigung zu vermeiden. Unterm Strich bedeutet diese Verbesserung der Cleverness von Compilern, dass diese Tricks jetzt nicht mehr im C-Code verwendet werden müssen und die Lesbarkeit und Portabilität beeinträchtigen. Immer noch sehr gut, sie zu kennen, wenn Sie zB SSE-Vektorcode schreiben; oder in jeder Situation, in der Sie es schnell brauchen und es einen Trick gibt, den der Compiler nicht verwendet (z. B. GPU-Code).
Greggo
2
Ein weiteres gutes Beispiel: Sehr häufig if(x >= 1 && x <= 9)kann Folgendes getan werden, da das if( (unsigned)(x-1) <=(unsigned)(9-1)) Ändern von zwei bedingten Tests zu einem großen Geschwindigkeitsvorteil sein kann. insbesondere, wenn eine prädizierte Ausführung anstelle von Verzweigungen möglich ist. Ich habe dies jahrelang verwendet (wo dies gerechtfertigt war), bis ich vor ungefähr 10 Jahren bemerkte, dass Compiler begonnen hatten, diese Transformation im Optimierer durchzuführen, und dann hörte ich auf. Immer noch gut zu wissen, da es ähnliche Situationen gibt, in denen der Compiler die Transformation nicht für Sie durchführen kann. Oder wenn Sie an einem Compiler arbeiten.
Greggo
3
Gibt es einen Grund, warum Ihr "Byte" nur 7 Bits ist?
Mason Watmough
104

Bitweise Operationen, einschließlich Bitverschiebung, sind für Hardware auf niedriger Ebene oder eingebettete Programmierung von grundlegender Bedeutung. Wenn Sie eine Spezifikation für ein Gerät oder sogar einige binäre Dateiformate lesen, werden Bytes, Wörter und Wörter angezeigt, die in nicht byteorientierte Bitfelder unterteilt sind, die verschiedene interessante Werte enthalten. Der Zugriff auf diese Bitfelder zum Lesen / Schreiben ist die häufigste Verwendung.

Ein einfaches reales Beispiel in der Grafikprogrammierung ist, dass ein 16-Bit-Pixel wie folgt dargestellt wird:

  bit | 15| 14| 13| 12| 11| 10| 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1  | 0 |
      |       Blue        |         Green         |       Red          |

Um den grünen Wert zu erreichen, würden Sie Folgendes tun:

 #define GREEN_MASK  0x7E0
 #define GREEN_OFFSET  5

 // Read green
 uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Erläuterung

Um den Wert von NUR Grün zu erhalten, der bei Offset 5 beginnt und bei 10 endet (dh 6 Bit lang), müssen Sie eine (Bit-) Maske verwenden, die bei Anwendung auf das gesamte 16-Bit-Pixel ergibt nur die Teile, die uns interessieren.

#define GREEN_MASK  0x7E0

Die entsprechende Maske ist 0x7E0, was in Binärform 0000011111100000 ist (was 2016 in Dezimalzahl ist).

uint16_t green = (pixel & GREEN_MASK) ...;

Um eine Maske anzuwenden, verwenden Sie den AND-Operator (&).

uint16_t green = (pixel & GREEN_MASK) >> GREEN_OFFSET;

Nach dem Anwenden der Maske erhalten Sie eine 16-Bit-Nummer, die eigentlich nur eine 11-Bit-Nummer ist, da sich das MSB im 11. Bit befindet. Grün ist eigentlich nur 6 Bit lang, daher müssen wir es mit einer Rechtsverschiebung (11 - 6 = 5) verkleinern, daher die Verwendung von 5 als Offset ( #define GREEN_OFFSET 5).

Ebenfalls üblich ist die Verwendung von Bitverschiebungen zur schnellen Multiplikation und Division durch Potenzen von 2:

 i <<= x;  // i *= 2^x;
 i >>= y;  // i /= 2^y;
Robottobor
quelle
1
0x7e0 ist dasselbe wie 11111100000, das 2016 dezimal ist.
Saheed
50

Bit Masking & Shifting

Bitverschiebung wird häufig in der Grafikprogrammierung auf niedriger Ebene verwendet. Zum Beispiel ein gegebener Pixelfarbwert, der in einem 32-Bit-Wort codiert ist.

 Pixel-Color Value in Hex:    B9B9B900
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Zum besseren Verständnis wird derselbe Binärwert angegeben, der mit welchen Abschnitten welchen Farbteil darstellt.

                                 Red     Green     Blue       Alpha
 Pixel-Color Value in Binary: 10111001  10111001  10111001  00000000

Angenommen, wir möchten den Grünwert der Farbe dieses Pixels ermitteln. Wir können diesen Wert leicht durch Maskieren und Verschieben erhalten .

Unsere Maske:

                  Red      Green      Blue      Alpha
 color :        10111001  10111001  10111001  00000000
 green_mask  :  00000000  11111111  00000000  00000000

 masked_color = color & green_mask

 masked_color:  00000000  10111001  00000000  00000000

Der logische &Operator stellt sicher, dass nur die Werte beibehalten werden, bei denen die Maske 1 ist. Das Letzte, was wir jetzt tun müssen, ist, den richtigen ganzzahligen Wert zu erhalten, indem alle diese Bits um 16 Stellen nach rechts verschoben werden (logische Rechtsverschiebung) .

 green_value = masked_color >>> 16

Et voilà, wir haben die Ganzzahl, die die Menge an Grün in der Farbe des Pixels darstellt:

 Pixels-Green Value in Hex:     000000B9
 Pixels-Green Value in Binary:  00000000 00000000 00000000 10111001
 Pixels-Green Value in Decimal: 185

Dies wird oft verwendet zum Codieren oder Decodieren Bildformate wie jpg, pngusw.

Basti Funck
quelle
Ist es nicht einfacher, Ihr Original, sagen wir 32bit cl_uint, als so etwas wie cl_uchar4 zu konvertieren und direkt auf das gewünschte Byte als * .s2 zuzugreifen?
David H Parry
27

Ein Problem ist, dass Folgendes implementierungsabhängig ist (gemäß dem ANSI-Standard):

char x = -1;
x >> 1;

x kann jetzt 127 (01111111) oder noch -1 (11111111) sein.

In der Praxis ist es normalerweise Letzteres.

AShelly
quelle
4
Wenn ich mich richtig erinnere, sagt der ANSI C-Standard ausdrücklich, dass dies implementierungsabhängig ist. Sie müssen daher die Dokumentation Ihres Compilers überprüfen, um zu sehen, wie sie implementiert ist, wenn Sie vorzeichenbehaftete Ganzzahlen in Ihrem Code nach rechts verschieben möchten.
Joe Pineda
Ja, ich wollte nur betonen, dass der ANSI-Standard selbst dies sagt. Es ist kein Fall, in dem Anbieter einfach nicht dem Standard folgen oder dass der Standard nichts über diesen speziellen Fall aussagt.
Joe Pineda
22

Ich schreibe nur Tipps und Tricks. Es kann bei Tests und Prüfungen nützlich sein.

  1. n = n*2:: n = n<<1
  2. n = n/2:: n = n>>1
  3. Prüfen, ob n eine Potenz von 2 ist (1,2,4,8, ...): prüfen !(n & (n-1))
  4. Erhalten des x- ten Teils von n:n |= (1 << x)
  5. Überprüfen, ob x gerade oder ungerade ist: x&1 == 0(gerade)
  6. Schalten Sie das n- te Bit von x um:x ^ (1<<n)
Ravi Prakash
quelle
Es müssen noch ein paar sein, die Sie inzwischen kennen?
Ryyker
@ryyker Ich habe noch ein paar hinzugefügt. Ich werde versuchen, es weiter zu aktualisieren :)
Ravi Prakash
Sind x und n 0 indiziert?
Reggaeguitar
Ad 5.: Was ist, wenn es sich um eine negative Zahl handelt?
Peter Mortensen
Können wir also schließen, dass 2 in binär wie 10 in dezimal ist? und Bitverschiebung ist wie das Addieren oder Subtrahieren einer weiteren Zahl hinter einer anderen Zahl in Dezimalzahl?
Willy Satrio Nugroho
8

Beachten Sie, dass in der Java-Implementierung die Anzahl der zu verschiebenden Bits von der Größe der Quelle abhängt.

Zum Beispiel:

(long) 4 >> 65

gleich 2. Sie könnten erwarten, dass eine 65-fache Verschiebung der Bits nach rechts alles auf Null setzen würde, aber es ist tatsächlich das Äquivalent von:

(long) 4 >> (65 % 64)

Dies gilt für <<, >> und >>>. Ich habe es nicht in anderen Sprachen ausprobiert.

Patrick Monkelban
quelle
Huh, interessant! In C ist dies ein technisch undefiniertes Verhalten . gcc 5.4.0gibt eine Warnung, gibt aber 2für 5 >> 65; auch.
Pizzapants184
2

Einige nützliche Bitoperationen / -manipulationen in Python.

Ich habe Ravi Prakashs Antwort in Python implementiert .

# Basic bit operations
# Integer to binary
print(bin(10))

# Binary to integer
print(int('1010', 2))

# Multiplying x with 2 .... x**2 == x << 1
print(200 << 1)

# Dividing x with 2 .... x/2 == x >> 1
print(200 >> 1)

# Modulo x with 2 .... x % 2 == x & 1
if 20 & 1 == 0:
    print("20 is a even number")

# Check if n is power of 2: check !(n & (n-1))
print(not(33 & (33-1)))

# Getting xth bit of n: (n >> x) & 1
print((10 >> 2) & 1) # Bin of 10 == 1010 and second bit is 0

# Toggle nth bit of x : x^(1 << n)
# take bin(10) == 1010 and toggling second bit in bin(10) we get 1110 === bin(14)
print(10^(1 << 2))
Peter Mortensen
quelle
-3

Beachten Sie, dass auf der Windows-Plattform nur eine 32-Bit-Version von PHP verfügbar ist.

Wenn Sie beispielsweise << oder >> um mehr als 31 Bit verschieben, sind die Ergebnisse unerwartet. Normalerweise wird die ursprüngliche Zahl anstelle von Nullen zurückgegeben, und es kann ein wirklich kniffliger Fehler sein.

Wenn Sie eine 64-Bit-Version von PHP (Unix) verwenden, sollten Sie natürlich eine Verschiebung um mehr als 63 Bit vermeiden. Beispielsweise verwendet MySQL jedoch den 64-Bit-BIGINT, sodass keine Kompatibilitätsprobleme auftreten sollten.

UPDATE: Unter PHP 7 Windows können PHP-Builds endlich vollständige 64-Bit-Ganzzahlen verwenden: Die Größe einer Ganzzahl ist plattformabhängig, obwohl ein Maximalwert von etwa zwei Milliarden der übliche Wert ist (das sind 32 Bit mit Vorzeichen). 64-Bit-Plattformen haben normalerweise einen Maximalwert von etwa 9E18, außer unter Windows vor PHP 7, wo es immer 32-Bit war.

lukyer
quelle