Aritmética desplazamiento a la derecha da resultado falso?


Debo estar absolutamente loco aquí, pero gcc 4.7.3 en mi máquina está dando el resultado más absurdo. Aquí está el código exacto que estoy probando:

#include <iostream>

using namespace std;

int main(){
  unsigned int b = 100000;
  cout << (b>>b) << endl;
  b = b >> b;
  cout << b << endl;
  b >>= b;
  cout << b << endl;
  return 0;
}

Ahora, cualquier número que está desplazado a la derecha por sí mismo debería resultar en 0 (n/(2^n) == 0 con entero dividir, n>1, y positivo / sin signo ), pero de alguna manera aquí está mi salida:

100000
100000
100000

¿Estoy loco? ¿Qué podría estar pasando?

Author: Failed Scientist, 2013-10-28

2 answers

En C++ como en C, los cambios están limitados al tamaño (en bits) del valor desplazado. Por ejemplo, si unsigned int es 32 bits, entonces un desplazamiento de más de 31 es indefinido.

En la práctica, un resultado común es que los 5 bits menos significativos de la cantidad de desplazamiento se utilizan y los bits de orden superior se ignoran; esto se debe a que el compilador produce una instrucción de máquina que hace exactamente eso (por ejemplo, SHR en x86).

En este caso, el valor shift es 100000 (decimal) que pasa a ser 11000011010100000 en binario - los 5 bits inferiores son cero. Por lo tanto, usted está consiguiendo efectivamente un cambio por 0. Sin embargo, no debes confiar en esto; técnicamente, lo que estás viendo es un comportamiento indefinido.

Referencias:

Para C, N1570 sección 6.5.7:

Si el valor del operando derecho es negativo o es mayor que or igual al ancho del operando izquierdo promovido, el comportamiento es indefinido.

Para C++, N3690 sección 5.8 "[expr.shift]":

El comportamiento es indefinido si el operando derecho es negativo, o mayor que o igual a la longitud en bits del operando izquierdo promovido.

N1570 es un borrador, casi idéntico al estándar ISO C11 lanzado; esta cláusula ha sido más o menos la misma desde el estándar ANSI C de 1989.

N3690 es un borrador reciente del estándar C++; no estoy seguro de si es el mejor para usar, pero de nuevo, esta cláusula no ha cambiado.

 47
Author: davmac,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-31 11:56:21

Está invocando undefined behavior si desplaza más de la longitud de bits del operando izquierdo, la sección draft C++ standard 5.8 Operadores de turno párrafo 1 dice ( énfasis mío):

Los operandos serán del tipo de enumeración integral o sin ámbito y se realizarán promociones integrales. El tipo del resultado es el del operando izquierdo promovido. El comportamiento es indefinido si el operando derecho es negativo, o mayor o igual a la longitud en bits del operando izquierdo promovido.

Interesante observar que tanto gcc y clang may generate a warning for this code if the shift amount if a literal :

cout << (b>> 100000) ;

O si b es una const , la advertencia para gcc es la siguiente:

warning: right shift count >= width of type [enabled by default]

Como MSalters señala en los comentarios a la pregunta, es posible que ni siquiera podamos confiar en esta advertencia ya que esto es indefinido comportamiento , que es consistente con la nota de estándares sobre comportamiento indefinido en la sección términos y definiciones que dice:

Nota: [... El comportamiento indefinido permisible va desde ignorar la situación completamente con resultados impredecibles, hasta comportarse durante la traducción o ejecución del programa de una manera documentada característica del entorno (con o sin la emisión de un mensaje de diagnóstico), hasta terminar una traducción o ejecución (con la emisión de un mensaje de diagnóstico). [...]

Detalles específicos de la plataforma

Una explicación potencial para la aparente falta de un cambio en el código de ejemplo puede ser porque en algunas plataformas el recuento de cambios será enmascarado a 5 bits por ejemplo en una arquitectura x86 podemos ver el Intel® 64 y IA-32 Arquitecturas Manual del Desarrollador de Software sección SAL/SAR/SHL/SHR-Shift en el IA-32 Arquitectura Compatibilidad la sección dice:

El 8086 no enmascara el recuento de cambios. Sin embargo, todos los demás procesadores IA-32 (comenzando con el procesador Intel 286) enmascaran el recuento de cambios a 5 bits, lo que resulta en un recuento máximo de 31. [...]

 32
Author: Shafik Yaghmour,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-28 17:19:50