Gehen Sie zu den Operatoren << und >>

124

Könnte mir bitte jemand die Verwendung von <<und >>in Go erklären ? Ich denke, es ist ähnlich wie in einigen anderen Sprachen.

brianoh
quelle

Antworten:

166

Die super (möglicherweise über) vereinfachte Definition wird nur <<für "mal 2" und >>für "geteilt durch 2" verwendet - und die Zahl danach ist wie oft.

So n << xist "n mal 2, x mal". Und y >> zist "y geteilt durch 2, z-mal".

Zum Beispiel 1 << 5ist "1 mal 2, 5 mal" oder 32. Und 32 >> 5ist "32 geteilt durch 2, 5 mal" oder 1.

Alle anderen Antworten geben die technischere Definition, aber niemand hat sie wirklich unverblümt dargelegt, und ich dachte, Sie möchten das vielleicht.

Peter Oram
quelle
7
Dies ist eine großartige Antwort. Das hat es wirklich in meinem Kopf verfestigt, danke.
Sam Orozco
2
So sollten Antworten sein.
Utsav Gupta
103

Aus der Spezifikation unter http://golang.org/doc/go_spec.html geht hervor , dass es sich zumindest bei Ganzzahlen um eine binäre Verschiebung handelt. Beispiel: Binär 0b00001000 >> 1 wäre 0b00000100 und 0b00001000 << 1 wäre 0b00010000.


Go akzeptiert anscheinend die 0b-Notation für binäre Ganzzahlen nicht. Ich habe es nur für das Beispiel verwendet. In Dezimalzahlen ist 8 >> 1 4 und 8 << 1 16. Das Verschieben nach links um eins entspricht der Multiplikation mit 2, und das Verschieben nach rechts um eins entspricht dem Teilen durch zwei, wobei der Rest verworfen wird.

jcomeau_ictx
quelle
4
Gute Antwort. Es macht sehr viel Sinn, wenn ich denke, dass ich dies im Code gesehen habe, der sich mit Potenzen von 2 (1 << power = 2 ^ power) befasst
Stephen Smith
6
Ich denke, dies wäre die vollständige Gleichung: (x << n == x * 2 ^ n) (x >> n == x * 2 ^ (- n))
MondayPaper
nette antwort, ich binärverschiebung schien zunächst mühsam, aber die konvertierung des
wertes
30

Die Operatoren << und >> sind Go-Arithmetik-Operatoren .

<<   left shift             integer << unsigned integer
>>   right shift            integer >> unsigned integer

Die Verschiebungsoperatoren verschieben den linken Operanden um die durch den rechten Operanden angegebene Verschiebungszahl. Sie implementieren arithmetische Verschiebungen, wenn der linke Operand eine vorzeichenbehaftete Ganzzahl ist, und logische Verschiebungen, wenn es sich um eine vorzeichenlose Ganzzahl handelt. Die Anzahl der Schichten muss eine Ganzzahl ohne Vorzeichen sein. Es gibt keine Obergrenze für die Schichtanzahl. Verschiebungen verhalten sich so, als ob der linke Operand bei einer Verschiebungszahl von n n-mal um 1 verschoben wird. Infolgedessen ist x << 1 dasselbe wie x * 2 und x >> 1 dasselbe wie x / 2, jedoch in Richtung negativer Unendlichkeit abgeschnitten.

peterSO
quelle
10

Sie sind im Grunde genommen arithmetische Operatoren und in anderen Sprachen ist dies das gleiche Beispiel für PHP, C, Go

GEHEN

package main

import (
    "fmt"
)

func main() {
    var t , i uint
    t , i = 1 , 1

    for i = 1 ; i < 10 ; i++ {
        fmt.Printf("%d << %d = %d \n", t , i , t<<i)
    }


    fmt.Println()

    t = 512
    for i = 1 ; i < 10 ; i++ {
        fmt.Printf("%d >> %d = %d \n", t , i , t>>i)
    }

}

GO Demo

C.

#include <stdio.h>
int main()
{

    int t = 1 ;
    int i = 1 ;

    for(i = 1; i < 10; i++) {
        printf("%d << %d = %d \n", t, i, t << i);
    }

        printf("\n");

    t = 512;

    for(i = 1; i < 10; i++) {
        printf("%d >> %d = %d \n", t, i, t >> i);
    }    

  return 0;
}

C Demo

PHP

$t = $i = 1;

for($i = 1; $i < 10; $i++) {
    printf("%d << %d = %d \n", $t, $i, $t << $i);
}

print PHP_EOL;

$t = 512;

for($i = 1; $i < 10; $i++) {
    printf("%d >> %d = %d \n", $t, $i, $t >> $i);
}

PHP Demo

Sie würden alle ausgeben

1 << 1 = 2 
1 << 2 = 4 
1 << 3 = 8 
1 << 4 = 16 
1 << 5 = 32 
1 << 6 = 64 
1 << 7 = 128 
1 << 8 = 256 
1 << 9 = 512 

512 >> 1 = 256 
512 >> 2 = 128 
512 >> 3 = 64 
512 >> 4 = 32 
512 >> 5 = 16 
512 >> 6 = 8 
512 >> 7 = 4 
512 >> 8 = 2 
512 >> 9 = 1 
Baba
quelle
7

Die << und >> von Go ähneln Verschiebungen (dh Division oder Multiplikation mit einer Potenz von 2) in anderen Sprachen, aber da Go eine sicherere Sprache als C / C ++ ist, erledigt es einige zusätzliche Arbeit, wenn die Anzahl der Schichten eine Zahl ist .

Verschiebungsanweisungen in x86-CPUs berücksichtigen nur 5 Bit (6 Bit auf 64-Bit-x86-CPUs) der Verschiebungsanzahl. In Sprachen wie C / C ++ übersetzt der Shift-Operator in einen einzelnen CPU-Befehl.

Der folgende Go-Code

x := 10
y := uint(1025)  // A big shift count
println(x >> y)
println(x << y)

druckt

0
0

während ein C / C ++ - Programm drucken würde

5
20

quelle
3
Für C- und C ++ - Verschiebungsoperatoren: "Das Verhalten ist undefiniert, wenn der rechte Operand negativ ist oder größer oder gleich der Länge des heraufgestuften linken Operanden in Bits ist." Die C- und C ++ - Standards garantieren nicht, dass C- und C ++ - Programme 5 und 20 drucken.
peterSO
@ PeterSO: Ja, du hast recht. Mein Standpunkt ist, dass jeder Programmiersprachenstandard eine konkrete Implementierung auf einer konkreten CPU haben muss. Im Kontext einer einzelnen CPU-Familie (x86-32) ist das Verhalten aller C / C ++ - Compiler (kann erwartet werden) dasselbe. Der Grund dafür ist, dass die Ausgabe von genau 1 SHL / SHR / etc-Anweisung zur Implementierung des Shift-Operators das Beste ist, was ein optimierender C / C ++ - Compiler tun kann, wenn der Kontext nichts über 'x' und 'y' aussagt. Wenn der Compiler weiß, dass der Code ein undefiniertes Verhalten aufweist, sollte er den Benutzer darüber informieren.
2
Ich bin nicht einverstanden. Sie sollten tragbaren Code schreiben. Sowohl Linux als auch Windows laufen unter ARM. Die Konzentration auf eine einzelne CPU-Familie ist kurzsichtig. Auch y ist eine Variable. Tatsächlich kennt der Compiler seine tatsächlichen Laufzeitwerte nicht.
PeterSO
@Atom Abgesehen von der Sprache, die absolut keine Garantie dafür gibt, was passieren wird, kann das undefinierte Verhalten auch auf einem einzelnen Computer mit einem einzelnen Compiler variieren, wenn Sie beispielsweise die Kompilierungsoptionen ändern (z. B. einen optimierten Build). Sich in irgendeiner Weise darauf zu verlassen, ist gefährlich falsch, IMO.
Paul Hankin
@ Anonym Ja, aber das ist nur Theorie. Können Sie ein konkretes Beispiel geben, bei dem das Ändern der Kompilierungsoptionen zu einem unterschiedlichen Verhalten von <<oder >>in C / C ++ führt?
6

<<ist Linksverschiebung. >>ist eine vorzeichenverlängernde Rechtsverschiebung, wenn der linke Operand eine vorzeichenbehaftete Ganzzahl ist, und eine nullverlängerende Rechtsverschiebung, wenn der linke Operand eine vorzeichenlose Ganzzahl ist.

Zum besseren Verständnis >>denken

var u uint32 = 0x80000000;
var i int32 = -2;

u >> 1;  // Is 0x40000000 similar to >>> in Java
i >> 1;  // Is -1 similar to >> in Java

Wenn sie also auf eine vorzeichenlose Ganzzahl angewendet werden, werden die Bits links mit Null gefüllt, während bei Anwendung auf eine vorzeichenbehaftete Ganzzahl die Bits links mit dem Bit ganz links gefüllt werden (das 1 ist, wenn die vorzeichenbehaftete Ganzzahl gemäß 2 negativ ist ergänzen).

Mike Samuel
quelle
3

Wenn wir in der Dezimalmathematik mit 10 multiplizieren oder dividieren , bewirken wir die Nullen am Ende der Zahl.

In der Binärdatei hat 2 den gleichen Effekt. Also fügen wir am Ende eine Null hinzu oder entfernen die letzte Ziffer

Robert King
quelle