¿El desplazamiento a la derecha aritmético da un 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  using namespace std; int main(){ unsigned int b = 100000; cout <>b) <> b; cout << b <>= b; cout << b << endl; return 0; } 

Ahora, cualquier número que esté desplazado a la derecha debería dar como resultado 0 ( n/(2^n) == 0 con división de enteros , n>1 y positivo / sin signo ), pero de alguna manera aquí está mi resultado:

 100000 100000 100000 

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

En C ++ como en C, los desplazamientos se limitan al tamaño (en bits) del valor desplazado. Por ejemplo, si unsigned int es 32 bits, entonces un cambio de más de 31 no está definido.

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

En este caso, el valor de desplazamiento es 100000 (decimal) que pasa a ser 11000011010100000 en binario – los 5 bits inferiores son cero. Por lo tanto, efectivamente está obteniendo un cambio por 0. Sin embargo, no debe 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 o igual que el ancho del operando izquierdo promovido, el comportamiento no está definido.

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

El comportamiento no está definido si el operando derecho es negativo, o mayor o igual que 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 prácticamente 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 una vez más, esta cláusula no ha cambiado.

Estás invocando un comportamiento indefinido si cambias más allá de la longitud del bit del operando izquierdo, el borrador de la sección estándar de C ++ 5.8 Operadores de turno, párrafo 1 dice ( énfasis mío ):

Los operandos deben ser de tipo de enumeración integral o no coordinada y se realizan promociones integrales. El tipo de resultado es el del operando izquierdo promovido. El comportamiento no está definido si el operando derecho es negativo, o mayor o igual que la longitud en bits del operando izquierdo promovido.

Es interesante notar que tanto gcc como clang pueden generar una advertencia para este código si la cantidad de desplazamiento es literal :

 cout << (b>> 100000) ; 

o si b es un 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 no podamos siquiera confiar en esta advertencia ya que este es un comportamiento indefinido , que es consistente con la nota de estándares sobre el comportamiento indefinido en la sección de términos y definiciones que dice:

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

Detalles específicos de la plataforma

Una posible explicación de la aparente falta de un cambio en el código de ejemplo puede deberse a que en algunas plataformas el recuento de cambios estará enmascarado a 5 bits por ejemplo, en una architecture x86 . Podemos ver las architectures Intel® 64 e IA-32 Software Developer Manual La sección SAL / SAR / SHL / SHR-Shift en la sección de compatibilidad de architecture IA-32 dice:

El 8086 no oculta el recuento de turnos. Sin embargo, todos los demás procesadores IA-32 (comenzando con el procesador Intel 286) enmascaran la cuenta de cambios a 5 bits, lo que da como resultado un conteo máximo de 31. […]