
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é?

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.

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)
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:
-
Un desplazamiento ‘aritmético’ a la derecha (que a menudo tiene ASR o SRA mnemotécnicos) que funciona como expliqué.
-
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

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