Ir a operadores << y >>

124

¿Podría alguien explicarme el uso de <<y >>en Go? Supongo que es similar a algunos otros idiomas.

brianoh
fuente

Respuestas:

169

La definición súper (posiblemente más) simplificada es solo que <<se usa para "multiplicar por 2" y >>es para "dividir por 2", y el número que sigue es cuántas veces.

También lo n << xes "n veces 2, x veces". Y y >> zes "y dividido por 2, z veces".

Por ejemplo, 1 << 5es "1 por 2, 5 veces" o 32. Y 32 >> 5es "32 dividido por 2, 5 veces" o 1.

Todas las otras respuestas dan la definición más técnica, pero nadie lo expuso de manera muy directa y pensé que tal vez quisieras eso.

Peter Oram
fuente
7
Esta es una respuesta genial. Esto realmente lo solidificó en mi cabeza, gracias.
Sam Orozco
2
Así deben ser las respuestas.
Utsav Gupta
103

De la especificación en http://golang.org/doc/go_spec.html , parece que al menos con números enteros, es un cambio binario. por ejemplo, el binario 0b00001000 >> 1 sería 0b00000100 y 0b00001000 << 1 sería 0b00010000.


Go aparentemente no acepta la notación 0b para enteros binarios. Solo lo estaba usando como ejemplo. En decimal, 8 >> 1 es 4 y 8 << 1 es 16. Desplazarse a la izquierda en uno es lo mismo que multiplicar por 2, y desplazar a la derecha en uno es lo mismo que dividir entre dos, descartando cualquier resto.

jcomeau_ictx
fuente
4
Gran respuesta. Tiene mucho sentido cuando creo que estaba viendo esto en el código que trata con potencias de 2 (1 << potencia = 2 ^ potencia)
Stephen Smith
6
Creo que esta sería la ecuación completa: (x << n == x * 2 ^ n) (x >> n == x * 2 ^ (- n))
MondayPaper
buena respuesta, el cambio binario parecía problemático al principio, pero convertir el valor a decimal con potencias a 2 ayuda a obtenerlo
minhajul
31

Los operadores << y >> son Operadores aritméticos Go .

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

Los operadores de desplazamiento desplazan el operando izquierdo según el recuento de desplazamientos especificado por el operando derecho. Implementan cambios aritméticos si el operando izquierdo es un entero con signo y cambios lógicos si es un entero sin signo. El recuento de turnos debe ser un número entero sin signo. No hay límite superior en el recuento de turnos. Los cambios se comportan como si el operando de la izquierda se cambiara n veces en 1 para un recuento de cambios de n. Como resultado, x << 1 es lo mismo que x * 2 y x >> 1 es lo mismo que x / 2 pero truncado hacia el infinito negativo.

peterSO
fuente
10

Son básicamente operadores aritméticos y es lo mismo en otros lenguajes aquí hay un ejemplo básico de PHP, C, Go

VAMOS

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

}

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

Demostración de PHP

Todos saldrían

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

Los << y >> de Go son similares a los cambios (es decir: división o multiplicación por una potencia de 2) en otros idiomas, pero como Go es un lenguaje más seguro que C / C ++, hace un trabajo adicional cuando el recuento de turnos es un número .

Las instrucciones de cambio en CPU x86 consideran solo 5 bits (6 bits en CPU x86 de 64 bits) del recuento de cambios. En lenguajes como C / C ++, el operador de turno se traduce en una sola instrucción de CPU.

El siguiente código de Go

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

huellas dactilares

0
0

mientras que un programa C / C ++ imprimiría

5
20

fuente
3
Para los operadores de desplazamiento de C y C ++, "El comportamiento no está definido si el operando derecho es negativo, o mayor o igual que la longitud en bits del operando izquierdo promovido". Los estándares C y C ++ no garantizan que los programas C y C ++ imprimirán 5 y 20.
peterSO
@peterSO: Sí, tienes razón. Mi punto de vista es que cada estándar de lenguaje de programación debe tener una implementación concreta en una CPU concreta. En el contexto de una sola familia de CPU (x86-32), el comportamiento de todos los compiladores de C / C ++ es (se puede esperar que sea) el mismo. La razón de esto es que emitir exactamente 1 instrucción SHL / SHR / etc para implementar el operador de cambio es lo mejor que puede hacer un compilador C / C ++ optimizador cuando el contexto no le dice nada sobre 'x' e 'y'. Y, si el compilador sabe a ciencia cierta que el código tiene un comportamiento indefinido, debe informar al usuario al respecto.
2
Estoy en desacuerdo. Debería escribir código portátil. Tanto Linux como Windows se ejecutan en ARM. Centrarse en una sola familia de CPU es miope. Además, y es una variable. De hecho, el compilador no tiene conocimiento de sus valores reales en tiempo de ejecución.
peterSO
@Atom Aparte de que el lenguaje no ofrece absolutamente ninguna garantía sobre lo que sucederá, es probable que el comportamiento indefinido varíe incluso en una sola máquina con un solo compilador, si, por ejemplo, cambia las opciones de compilación (por ejemplo, una compilación optimizada). En mi opinión, confiar en él de cualquier manera es peligrosamente incorrecto.
Paul Hankin
@Anónimo Sí, pero eso es solo teoría. ¿Puede proporcionar un ejemplo concreto en el que cambiar las opciones de compilación conduzca a un comportamiento diferente de <<o >>en C / C ++?
6

<<es desplazamiento a la izquierda. >>es un desplazamiento a la derecha con extensión de signo cuando el operando de la izquierda es un entero con signo, y es un desplazamiento a la derecha de extensión cero cuando el operando de la izquierda es un entero sin signo.

Para entender mejor, >>piensa en

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

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

Entonces, cuando se aplica a un entero sin signo, los bits de la izquierda se rellenan con cero, mientras que cuando se aplican a un entero con signo, los bits de la izquierda se rellenan con el bit más a la izquierda (que es 1 cuando el entero con signo es negativo según 2 complemento).

Mike Samuel
fuente
3

En matemáticas decimales , cuando multiplicamos o dividimos por 10 , aplicamos los ceros al final del número.

En binario , 2 tiene el mismo efecto. Entonces estamos agregando un cero al final o quitando el último dígito

Robert King
fuente