Idź do operatorów << i >>

124

Czy ktoś mógłby mi wyjaśnić, jak używać <<i >>w Go? Myślę, że jest podobny do niektórych innych języków.

brianoh
źródło

Odpowiedzi:

169

Super (prawdopodobnie nadmiernie) uproszczona definicja jest po prostu <<używana do wyrażenia „razy 2” i >>„podzielona przez 2” - a liczba po niej to ile razy.

Czyli n << x„n razy 2, x razy”. I y >> zjest "y podzielone przez 2, z razy".

Na przykład 1 << 5to „1 razy 2, 5 razy” lub 32. A 32 >> 5to „32 podzielone przez 2, 5 razy” lub 1.

Wszystkie inne odpowiedzi podają bardziej techniczną definicję, ale nikt nie przedstawił jej naprawdę dosadnie i pomyślałem, że możesz tego chcieć.

Peter Oram
źródło
7
To świetna odpowiedź. To naprawdę utkwiło mi w głowie, dzięki.
Sam Orozco
2
Tak powinny wyglądać odpowiedzi.
Utsav Gupta
103

Ze specyfikacji na http://golang.org/doc/go_spec.html wynika, że ​​przynajmniej w przypadku liczb całkowitych jest to przesunięcie binarne. na przykład binarne 0b00001000 >> 1 będzie równe 0b00000100, a 0b00001000 << 1 będzie równe 0b00010000.


Go najwyraźniej nie akceptuje notacji 0b dla binarnych liczb całkowitych. Użyłem go tylko jako przykład. W systemie dziesiętnym 8 >> 1 to 4, a 8 << 1 to 16. Przesunięcie w lewo o jeden jest tym samym, co pomnożenie przez 2, a przesunięcie w prawo o jeden to to samo, co podzielenie przez dwa, z odrzuceniem pozostałej części.

jcomeau_ictx
źródło
4
Świetna odpowiedź. Ma to sens, gdy myślę, że widziałem to w kodzie zajmującym się potęgami 2 (1 << potęga = 2 ^ potęga)
Stephen Smith
6
Myślę, że byłoby to pełne równanie: (x << n == x * 2 ^ n) (x >> n == x * 2 ^ (- n))
poniedziałekPapier,
dobra odpowiedź, przesunięcie binarne wydawało się początkowo kłopotliwe, ale konwersja wartości na dziesiętną z potęgami do 2 pomaga dobrze to uzyskać
minhajul
31

Operatory << i >> to operatory arytmetyczne Go .

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

Operatory przesunięcia przesuwają lewy operand o liczbę przesunięć określoną przez prawy operand. Realizują przesunięcia arytmetyczne, jeśli lewy operand jest liczbą całkowitą ze znakiem, a przesunięcia logiczne, jeśli jest liczbą całkowitą bez znaku. Liczba przesunięć musi być liczbą całkowitą bez znaku. Nie ma górnej granicy liczby zmian. Przesunięcia zachowują się tak, jakby lewy operand został przesunięty n razy o 1 dla liczby przesunięć wynoszącej n. W rezultacie x << 1 to to samo, co x * 2, a x >> 1 to to samo, co x / 2, ale obcięte w kierunku ujemnej nieskończoności.

peterSO
źródło
10

Są to w zasadzie operatory arytmetyczne i tak samo jest w innych językach. Oto podstawowy przykład PHP, C, Go

UDAĆ SIĘ

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

do

#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);
}

Demo PHP

Wszystkie wyjdą

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
źródło
7

Go << i >> są podobne do przesunięć (to znaczy: dzielenia lub mnożenia przez potęgę 2) w innych językach, ale ponieważ Go jest bezpieczniejszym językiem niż C / C ++, wykonuje dodatkową pracę, gdy liczba zmian jest liczbą .

Instrukcje zmiany w procesorach x86 uwzględniają tylko 5 bitów (6 bitów w 64-bitowych procesorach x86) liczby przesunięć. W językach takich jak C / C ++ operator shift tłumaczy na pojedynczą instrukcję procesora.

Poniższy kod Go

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

wydruki

0
0

podczas gdy program C / C ++ będzie drukował

5
20

źródło
3
W przypadku operatorów przesunięcia w językach C i C ++: „Zachowanie jest nieokreślone, jeśli prawy operand jest ujemny lub większy lub równy długości w bitach promowanego lewego operandu”. Standardy C i C ++ nie gwarantują, że programy C i C ++ będą drukować 5 i 20.
peterSO,
@peterSO: Tak, masz rację. Mój punkt widzenia jest taki, że każdy standard języka programowania musi mieć konkretną implementację na konkretnym procesorze. W kontekście pojedynczej rodziny procesorów (x86-32) zachowanie wszystkich kompilatorów C / C ++ jest (można się spodziewać) takie same. Powodem tego jest to, że wyemitowanie dokładnie 1 instrukcji SHL / SHR / etc w celu zaimplementowania operatora zmiany jest najlepszą rzeczą, jaką może zrobić optymalizujący kompilator C / C ++, gdy kontekst nie mówi mu nic o „x” i „y”. A jeśli kompilator wie na pewno, że kod ma niezdefiniowane zachowanie, powinien poinformować o tym użytkownika.
2
Nie zgadzam się. Powinieneś napisać kod przenośny. Zarówno Linux, jak i Windows działają na ARM. Skoncentrowanie się na jednej rodzinie procesorów jest krótkowzroczne. Również y jest zmienną. W rzeczywistości kompilator nie ma żadnej wiedzy o swoich rzeczywistych wartościach w czasie wykonywania.
peterSO
@Atom Oprócz języka nie dającego absolutnie żadnych gwarancji co do tego, co się stanie, niezdefiniowane zachowanie może się różnić nawet na pojedynczej maszynie z jednym kompilatorem, jeśli na przykład zmienisz opcje kompilacji (np. Zoptymalizowana kompilacja). Poleganie na nim w jakikolwiek sposób jest niebezpiecznie błędem IMO.
Paul Hankin
@ Anonimowy Tak, ale to tylko teoria. Czy możesz podać konkretny przykład, w którym zmiana opcji kompilacji prowadzi do innego zachowania C / C ++ <<lub >>w C ++?
6

<<to przesunięcie w lewo. >>jest przesunięciem w prawo rozszerzającym znak, gdy lewy operand jest liczbą całkowitą ze znakiem, i przesunięciem w prawo rozszerzającym o zero, gdy lewy operand jest liczbą całkowitą bez znaku.

Aby lepiej zrozumieć, >>pomyśl

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

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

Zatem po zastosowaniu do liczby całkowitej bez znaku bity po lewej stronie są wypełnione zerem, podczas gdy w przypadku liczby całkowitej ze znakiem, bity po lewej stronie są wypełniane bitem znajdującym się najbardziej po lewej stronie (czyli 1, gdy liczba całkowita ze znakiem jest ujemna, jak na 2 komplement).

Mike Samuel
źródło
3

W matematyce dziesiętnej , gdy mnożymy lub dzielimy przez 10 , wpływamy na zera na końcu liczby.

W binarnym , 2 ma ten sam efekt. Więc dodajemy zero na końcu lub usuwamy ostatnią cyfrę

Robert King
źródło