Operadores Go e <<

124

Alguém poderia me explicar o uso <<e >>no Go? Eu acho que é semelhante a algumas outras línguas.

brianoh
fonte

Respostas:

168

A definição super (possivelmente super) simplificada é apenas <<usada para "tempos 2" e >>é para "dividido por 2" - e o número depois disso é quantas vezes.

O mesmo n << xacontece com "n vezes 2, x vezes". E y >> zé "y dividido por 2, z vezes".

Por exemplo, 1 << 5é "1 vezes 2, 5 vezes" ou 32. E 32 >> 5é "32 dividido por 2, 5 vezes" ou 1.

Todas as outras respostas dão uma definição mais técnica, mas ninguém a definiu realmente sem rodeios e achei que você poderia querer isso.

Peter Oram
fonte
7
Esta é uma ótima resposta. Isso realmente solidificou na minha cabeça, obrigado.
Sam Orozco
2
É assim que as respostas devem ser.
Utsav Gupta
103

A partir das especificações em http://golang.org/doc/go_spec.html , parece que pelo menos com números inteiros, é uma mudança binária. por exemplo, o binário 0b00001000 >> 1 seria 0b00000100 e 0b00001000 << 1 seria 0b00010000.


Go aparentemente não aceita a notação 0b para números inteiros binários. Eu estava apenas usando-o como exemplo. Em decimal, 8 >> 1 é 4 e 8 << 1 é 16. Mudar para a esquerda por um é o mesmo que multiplicar por 2, e mudar para a direita por um é o mesmo que dividir por dois, descartando o restante.

jcomeau_ictx
fonte
4
Ótima resposta. Faz muito sentido quando penso que eu estava vendo isso no tráfico de código com potências de 2 (1 << potência = 2 ^ poder)
Stephen Smith
6
Eu acho que esta seria a equação completa: (x << n == x * 2 ^ n) (x >> n == x * 2 ^ (- n))
MondayPaper
resposta bom, eu deslocamento binário parecia problemático no começo, mas converter o valor para decimal com poderes para 2 ajuda bem para obtê-lo
minhajul
30

Os operadores << e >> são operadores aritméticos Go .

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

Os operadores de turno deslocam o operando esquerdo pela contagem de turnos especificada pelo operando direito. Eles implementam turnos aritméticos se o operando esquerdo for um número inteiro assinado e turnos lógicos se for um número inteiro não assinado. A contagem de turnos deve ser um número inteiro não assinado. Não há limite superior na contagem de turnos. Os turnos se comportam como se o operando esquerdo fosse deslocado n vezes por 1 para uma contagem de turnos de n. Como resultado, x << 1 é o mesmo que x * 2 e x >> 1 é o mesmo que x / 2, mas truncado para o infinito negativo.

peterSO
fonte
10

Eles são basicamente operadores aritméticos e é o mesmo em outras línguas. Aqui está um exemplo básico de PHP, C, Go

IR

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

Demonstração PHP

Todos eles produziriam

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
fonte
7

Os << e >> de Go são semelhantes aos turnos (isto é: divisão ou multiplicação por uma potência de 2) em outros idiomas, mas como o Go é uma linguagem mais segura que o C / C ++, ele realiza algum trabalho extra quando a contagem de turnos é um número .

As instruções de turno em CPUs x86 consideram apenas 5 bits (6 bits em CPUs x86 de 64 bits) da contagem de turnos. Em idiomas como C / C ++, o operador shift se traduz em uma única instrução de CPU.

O seguinte código Go

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

impressões

0
0

enquanto um programa C / C ++ seria impresso

5
20

fonte
3
Para operadores de deslocamento C e C ++, "O comportamento é indefinido se o operando direito for negativo ou maior ou igual ao comprimento em bits do operando esquerdo promovido". Os padrões C e C ++ não garantem que os programas C e C ++ sejam impressos em 5 e 20.
peterSO
@ PeterSO: Sim, você está certo. Meu ponto de vista é que cada padrão de linguagem de programação precisa ter uma implementação concreta em uma CPU concreta. No contexto de uma única família de CPU (x86-32), o comportamento de todos os compiladores C / C ++ é (pode-se esperar que seja) o mesmo. A razão para isso é que emitir exatamente 1 instrução SHL / SHR / etc para implementar o operador shift é a melhor coisa que um compilador C / C ++ otimizado pode fazer quando o contexto não diz nada sobre 'x' e 'y'. E, se o compilador souber de fato que o código tem um comportamento indefinido, ele deverá informar o usuário sobre isso.
2
Discordo. Você deve escrever um código portátil. Tanto o Linux quanto o Windows são executados no ARM. O foco em uma única família de CPU é míope. Além disso, y é uma variável. De fato, o compilador não tem conhecimento de seus valores reais de tempo de execução.
peterSO
@Atom Além do idioma que não oferece absolutamente nenhuma garantia sobre o que acontecerá, é provável que o comportamento indefinido varie mesmo em uma única máquina com um único compilador, se, por exemplo, você alterar as opções de compilação (por exemplo, uma compilação otimizada). Depender de alguma forma é perigosamente errado na IMO.
Paul Hankin
@ Anônimo Sim, mas isso é apenas teoria. Você pode fornecer um exemplo concreto em que a alteração das opções de compilação leva a comportamentos diferentes de <<ou >>no C / C ++?
6

<<é turno à esquerda. >>é deslocamento à direita com extensão de sinal quando o operando esquerdo é um número inteiro assinado e deslocamento à direita com extensão de zero quando o operando esquerdo é um número inteiro sem sinal.

Para entender melhor >>pensar em

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

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

Portanto, quando aplicados a um número inteiro não assinado, os bits à esquerda são preenchidos com zero, enquanto que, quando aplicados a um número inteiro assinado, os bits à esquerda são preenchidos com o bit mais à esquerda (que é 1 quando o número inteiro assinado é negativo, conforme 2's complemento).

Mike Samuel
fonte
3

Em matemática decimal , quando multiplicamos ou dividimos por 10 , efetuamos os zeros no final do número.

Em binário , 2 tem o mesmo efeito. Então, estamos adicionando um zero ao final ou removendo o último dígito

Robert King
fonte