Desplazamiento de bits aritmético en un entero con signo

8 minutos de lectura

avatar de usuario
nueva letra

Estoy tratando de averiguar cómo funcionan exactamente los operadores aritméticos de cambio de bits en C y cómo afectará a los enteros de 32 bits con signo.

Para simplificar las cosas, digamos que trabajamos dentro de un byte (8 bits):

x = 1101.0101
MSB[ 1101.0101 ]LSB

Al leer otras publicaciones en Stack Overflow y algunos sitios web, encontré que:
<< se desplazará hacia MSB (a la izquierda, en mi caso) y llenará los bits LSB “vacíos” con 0.

Y >> cambiará hacia LSB (a la derecha, en mi caso) y llenará los bits “vacíos” con el bit MS

Asi que, x = x << 7 dará como resultado mover LSB a MSB y configurar todo a 0s.

1000.0000

Ahora, digamos que lo haría >> 7, Ultimo resultado. Esto resultaría en [0000.0010]? ¿Tengo razón?

¿Tengo razón sobre mis suposiciones sobre los operadores de turnos?

Acabo de probar en mi máquina, **

int x = 1;   //000000000......01

x = x << 31; //100000000......00

x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!) 

¿Por qué?

  • posible duplicado del operador Shift en C

    – Jens Gusted

    24/10/2010 a las 19:42

  • “¿Por qué?” Porque tu implementación dice eso Compruebe la documentación: es deber defina el comportamiento del desplazamiento a la derecha de un valor negativo (o no tiene un compilador de C).

    – pmg

    24/10/2010 a las 20:02

  • @pseudonym27 que tienes que encontrar en la documentación

    – letra nueva

    21 de noviembre de 2014 a las 18:55

  • @pseudonym27: el desplazamiento a la derecha está bien definido, el desplazamiento a la izquierda no. Considere el desplazamiento a la izquierda como multiplicaciones sucesivas por 2. Si alguna multiplicación se desborda tienes Comportamiento indefinido.

    – pmg

    22 de noviembre de 2014 a las 10:23

  • Él borrador de la Norma C11 (documento PDF) está disponible gratuitamente en línea.

    – pmg

    23 de noviembre de 2014 a las 10:26

avatar de usuario
Mateo Slattery

El desplazamiento a la derecha de un número con signo negativo tiene un comportamiento definido por la implementación.

Si sus 8 bits están destinados a representar un valor de 8 bits con signo (ya que está hablando de un “entero de 32 bits con signo” antes de cambiar a ejemplos de 8 bits), entonces tiene un número negativo. Desplazarlo a la derecha puede llenar bits “vacíos” con el MSB original (es decir, realizar una extensión de signo) o puede cambiar a ceros, según la plataforma y/o el compilador.

(El comportamiento definido por la implementación significa que el compilador hará algo sensato, pero de una manera dependiente de la plataforma; se supone que la documentación del compilador le dirá qué).


Un desplazamiento a la izquierda, si el número comienza negativo, o si la operación de desplazamiento cambiaría un 1 hacia o más allá del bit de signo, tiene un comportamiento indefinido (al igual que la mayoría de las operaciones en valores con signo que provocan un desbordamiento).

(Comportamiento indefinido significa que cualquier cosa podría pasar).


Las mismas operaciones en no firmado los valores están bien definidos en ambos casos: los bits “vacíos” se llenarán con 0.

  • Debe decirse que el desplazamiento a la derecha con semántica portátil estándar también se conoce como división por un literal de potencia de dos. La mayoría de los compiladores generarán cambios aritméticos para tales divisiones, lo que dará como resultado un código rápido que firma la extensión deseada.

    – Kuba no se ha olvidado de Mónica

    16 de junio de 2014 a las 17:51

  • @KubaOber: Los estándares modernos requieren que x/2 comportarse de manera diferente a un desplazamiento a la derecha en el caso en que x es un número impar negativo. Un desplazamiento aritmético a la derecha normal realizará una división piso (por lo que en los casos que no se desbordan (n+d)/d == (n/d)+1pero se requiere división para usar la división truncada, lo que evita que los compiladores usen un cambio simple.

    – Super gato

    9 de julio de 2014 a las 18:02

  • @KubaOber: debe tenerse en cuenta que el desplazamiento a la izquierda de un valor con signo solía ser una multiplicación de potencia de dos con signo, pero los compiladores más nuevos parecen haber desaprobado el soporte para el desplazamiento a la izquierda de números negativos.

    – Super gato

    16 de abril de 2015 a las 5:49

  • Podría ser bueno indicar que en los compiladores hipermodernos, “cualquier cosa” incluye reorganizar la lógica del programa para fingir que los valores negativos son positivos. Dado void foo(int x) { if (x >= 0) printf("Kaboom!"); int y= x<<4; } vocación foo(-1); muy probablemente podría imprimir “Kaboom!”.

    – Super gato

    16 de abril de 2015 a las 5:56

avatar de usuario
pmg

Las operaciones de desplazamiento bit a bit no están definidas para valores negativos

para ‘<<'

6.5.7/4 […] Si E1 tiene un tipo con signo y un valor no negativo, y E1×2E2 es representable en el tipo de resultado, entonces ese es el valor resultante; de lo contrario, el comportamiento no está definido.

y para ‘>>’

6.5.7/5 […] Si E1 tiene un tipo con signo y un valor negativo, el valor resultante está definido por la implementación.

Es una pérdida de tiempo estudiar el comportamiento de estas operaciones en números con signo en una implementación específica, porque no tiene garantía de que funcionará de la misma manera en cualquier otra implementación (una implementación es, por ejemplo, su compilador en su computadora con su parámetros de línea de comando específicos).

Es posible que ni siquiera funcione para una versión más antigua o más nueva del mismo compilador. El compilador podría incluso definir esos bits como aleatorios o indefinidos. Esto significaría que la misma secuencia de código podría producir resultados totalmente diferentes cuando se usa en todas sus fuentes o incluso depender de cosas como la optimización de ensamblaje u otro uso de registro. Si se encapsula en una función, es posible que ni siquiera produzca el mismo resultado en esos bits en dos llamadas consecutivas con los mismos argumentos.

Considerando solo valores no negativosel efecto del desplazamiento a la izquierda en 1 (expression << 1) es lo mismo que multiplicar la expresión por 2 (siempre que la expresión * 2 no se desborde) y el efecto de desplazamiento a la derecha por 1 (expression >> 1) es lo mismo que dividir por 2.

A partir de c++20 los operadores de desplazamiento bit a bit para enteros con signo están bien definidos.

El cambio a la izquierda a<<b es equivalente a a*2^b módulo 2^N donde N es el número de bits en el tipo resultante. En particular 1<<31 es de hecho el mas pequeño int valor.

El cambio correcto a>>b es equivalente a a/2^b, redondeado hacia abajo (es decir, hacia el infinito negativo). Entonces, por ejemplo -1>>10 == -1.

Para más detalles ver https://en.cppreference.com/w/cpp/language/operator_arithmetic .

(para los estándares más antiguos, consulte la respuesta de Matthew Slattery)

  • ¿Y qué tal golang, o cualquier otro idioma con un leve parecido a C?

    – mevets

    18 de junio de 2020 a las 18:43

  • @mevets: todos los cuerdos (o al menos las implementaciones sensatas de ellos) ya funcionaron de esta manera, con >> en tipos con signo requeridos para ser un desplazamiento aritmético a la derecha. C ++ finalmente está comenzando a documentar lo que todos quieren que hagan sus compiladores de todos modos, y qué lenguajes como Rust y Java han hecho desde el principio. Correcto aritmético garantizado, en lugar de definido por la implementación.

    – Peter Cordes

    30 de abril de 2021 a las 7:29

  • @mevets: consulte aquí con respecto a Python: realpython.com/python-bitwise-operators

    – Hari

    24 ene a las 20:04

Como otros dijeron, el cambio de valor negativo está definido por la implementación.

La mayoría de las implementaciones tratan el desplazamiento a la derecha con signo como piso (x/2norte) llenando desplazado en bits usando bit de signo. Es muy conveniente en la práctica, ya que esta operación es muy común. Por otro lado, si desplaza a la derecha un entero sin signo, los bits desplazados se pondrán a cero.

Mirando desde el lado de la máquina, la mayoría de las implementaciones tienen dos tipos de instrucciones de desplazamiento a la derecha:

  1. Un desplazamiento ‘aritmético’ a la derecha (que a menudo tiene ASR o SRA mnemotécnicos) que funciona como expliqué.

  2. Un desplazamiento ‘lógico’ a la derecha (que a menudo tiene mnemotécnicos LSR o SRL o SR) que funciona como espera.

La mayoría de los compiladores utilizan primero para los tipos firmados y segundo para los no firmados. Solo por conveniencia.

En el compilador de 32 bits

x = x >> 31;

aquí x es el entero con signo, por lo que el bit 32 es el bit de signo.

el valor x final es 100000…000. y el bit 32 indica el valor -ive.

aquí el valor x implementa el complemento de 1.

entonces x final es -32768

avatar de usuario
andreas haferburg

En mi i7:

uint64_t:

0xffffffffffffffff >> 0 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 1 is 0b0111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 2 is 0b0011111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 3 is 0b0001111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 4 is 0b0000111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 62 is 0b0000000000000000000000000000000000000000000000000000000000000011
0xffffffffffffffff >> 63 is 0b0000000000000000000000000000000000000000000000000000000000000001
0xffffffffffffffff >> 64 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 65 is 0b0111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 66 is 0b0011111111111111111111111111111111111111111111111111111111111111

int64_t -1

0xffffffffffffffff >> 0 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 1 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 2 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 3 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 4 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 62 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 63 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 64 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 65 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 66 is 0b1111111111111111111111111111111111111111111111111111111111111111

int64_t 2^63-1

0x7fffffffffffffff >> 0 is 0b0111111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 1 is 0b0011111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 2 is 0b0001111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 3 is 0b0000111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 4 is 0b0000011111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 62 is 0b0000000000000000000000000000000000000000000000000000000000000001
0x7fffffffffffffff >> 63 is 0b0000000000000000000000000000000000000000000000000000000000000000
0x7fffffffffffffff >> 64 is 0b0111111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 65 is 0b0011111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 66 is 0b0001111111111111111111111111111111111111111111111111111111111111

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad