tipos rotacion operadores logicos lenguaje ejemplos dev desplazamiento datos c++ language-lawyer shift

rotacion - ¿El desplazamiento a la izquierda de un entero con signo no está definido en C++ 03?



tipos de datos en c (2)

De acuerdo con C ++ 03, 5.8 / 2, el desplazamiento a la izquierda se define de la siguiente manera:

El valor de E1 << E2 es E1 (interpretado como un patrón de bits) posiciones de bits E2 desplazadas a la izquierda; los bits vacíos se rellenan con cero. Si E1 tiene un tipo sin signo, el valor del resultado es E1 multiplicado por la cantidad 2 aumentada a la potencia E2, módulo reducido ULONG_MAX + 1 si E1 tiene el tipo sin signo largo, UINT_MAX + 1 de lo contrario.

Lo que me molesta aquí es que los tipos no firmados se mencionan explícitamente, pero los tipos firmados se ignoran completamente. Compare esto con 5.8 / 3 que define el desplazamiento a la derecha:

El valor de E1 >> E2 es E1 posiciones de bit E2 desplazadas a la derecha. Si E1 tiene un tipo sin signo o si E1 tiene un tipo con signo y un valor no negativo, el valor del resultado es la parte integral del cociente de E1 dividido por la cantidad 2 elevada a la potencia E2. Si E1 tiene un tipo con signo y un valor negativo, el valor resultante se define por la implementación.

En 5.8 / 3, tanto los firmados como los no firmados se mencionan explícitamente, incluso los valores negativos que tienen valores negativos y los negativos negativos se mencionan por separado.

AFAIK cuando algo no está definido explícitamente en C ++ Standard, el comportamiento no está definido. También he visto esta pregunta , pero se enfoca en las diferencias entre C y C ++ y no parece tener una respuesta con la que todos estén de acuerdo.

¿El desplazamiento a la izquierda es un entero con signo definido en C ++ 03?


5.8 / 2 dice que lo interpreta como un patrón de bits, que solo depende de la implementación si, por alguna razón, su implementación no usa el complemento de 2, o si su compilador lo adivina (no lo hacen). C ++ 11 es más explícito, pero dice lo mismo.

Los enteros firmados usan lo que se conoce como complemento a 2. Básicamente, si cambia un bit a un entero con signo en 1, si es positivo y por debajo de 2 ^ (bits - 2) funcionará como si no estuviera firmado. Si está por encima de eso pero es positivo, creará un número negativo extraño que no guarda relación con el original. Si para empezar es negativo, posiblemente obtendrás un número negativo, posiblemente un número positivo.

Por ejemplo, si tenemos un entero con signo de 8 bits que representa -1:

11111111 // -1

Si cambiamos a la izquierda que terminamos con

11111110 // -2

Sin embargo, digamos que tenemos -120

10001000 // -120

Terminaremos con

00010000 // 16

Obviamente eso no es correcto!

Continuando, utilizando el número 65:

01000001 // 65

Desplazado a la izquierda, esto se convertirá en:

10000001 // -127

Lo que equivale a -127.

Sin embargo, el número 16:

00010000 // 16

La izquierda desplazada es

00100000 // 32

Como puede ver, "a veces funciona, a veces no", pero generalmente funciona si su número está por debajo de 2 ^ (bits-2) y a veces, pero no por lo general, si está por encima de - (2 ^ (bits-2)). Es decir, para cambiar a la izquierda en 1. Para cambiar a la izquierda en 2, quita otro bit. Etc.


Me gustaría agregar que las reglas cambiaron en C ++ 11.

En C ++ 11, el desplazamiento firmado a la izquierda de un número negativo siempre está indefinido , incluso si la máquina subyacente lo define para los valores que están dentro del rango. No está definido por la implementación , está indefinido . Esto significa que si haces esto, el compilador es libre de hacer lo que quiera, incluyendo eliminar un montón de tu código de forma inesperada. Esto contrasta con el desplazamiento con signo a la derecha de los números negativos, que está definido por la implementación , lo que significa que su resultado depende del tipo de máquina.

Clang''s -fsanitize=undefined modo -fsanitize=undefined intentos de cambiar los números negativos a la izquierda.