2012-04-02 8 views
5

Cuando se lee el título de la pregunta, asignar 2^31 a una variable entera de 32 bits con signo y sin signo da un resultado inesperado.Resultado extraño después de asignar 2^31 a una variable entera de 32 bits con signo y sin signo

Aquí es el programa corto (en C++), el cual hice para ver lo que está pasando:

#include <cstdio> 
using namespace std; 

int main() 
{ 
    unsigned long long n = 1<<31; 
    long long n2 = 1<<31; // this works as expected 
    printf("%llu\n",n); 
    printf("%lld\n",n2); 
    printf("size of ULL: %d, size of LL: %d\n", sizeof(unsigned long long), sizeof(long long)); 
    return 0; 
} 

Aquí está la salida:

MyPC/# c++ test.cpp -o test 
MyPC/# ./test 
18446744071562067968  <- Should be 2^31 right? 
-2147483648    <- This is correct (-2^31 because of the sign bit) 
size of ULL: 8, size of LL: 8 

Luego añade otra función p(), a ella :

void p() 
{ 
    unsigned long long n = 1<<32; // since n is 8 bytes, this should be legal for any integer from 32 to 63 
    printf("%llu\n",n); 
} 

En la compilación y ejecución, esto es lo que confunde d mí aún más:

MyPC/# c++ test.cpp -o test 
test.cpp: In function ‘void p()’: 
test.cpp:6:28: warning: left shift count >= width of type [enabled by default] 
MyPC/# ./test 
0 
MyPC/

¿Por qué el compilador quejarse valor de desplazamiento a la izquierda es demasiado grande? sizeof(unsigned long long) devuelve 8, ¿no significa eso que 2^63-1 es el valor máximo para ese tipo de datos?

Se me ocurrió que tal vez n * 2 y n < < 1, no siempre se comportan de la misma manera, así que probamos este:

void s() 
{ 
    unsigned long long n = 1; 
    for(int a=0;a<63;a++) n = n*2; 
    printf("%llu\n",n); 
} 

Esto da el valor correcto de 2^63 como la salida que es 9223372036854775808 (lo verifiqué usando python). Pero, ¿qué hay de malo en hacer una mierda de izquierda?

Un desplazamiento aritmético izquierda de n es equivalente a multiplicar por 2 n (siempre que el valor no se desborde)

- Wikipedia

El valor no está desbordando , solo aparecerá un signo menos ya que el valor es 2^63 (todos los bits están configurados).

Todavía no puedo entender qué está pasando con el turno de la izquierda, ¿alguien puede explicar esto?

PS: Este programa se ejecuta en una casa de moneda de 32 bits Linux sistema en funcionamiento (si eso ayuda)

+0

Esto debería ser 'unsigned long long n = 1ULL << 31;' – kirilloid

+0

dios! ¿Fue así de fácil? ¿Por qué no lo pensé? de todos modos, sí 1ULL << 31 funciona. ¡así que gracias! – Rushil

Respuesta

10

En esta línea:

unsigned long long n = 1<<32; 

El problema es que el literal 1 es de tipo int - que probablemente solo sea 32 bits Por lo tanto, el cambio lo empujará fuera de los límites.

El hecho de que esté almacenando en un tipo de datos más grande no significa que todo en la expresión se realice en ese tamaño mayor.

Así que para corregirlo, es necesario o bien lanzarlo hacia arriba o que sea una unsigned long long literal:

unsigned long long n = (unsigned long long)1 << 32; 
unsigned long long n = 1ULL << 32; 
+0

Con respecto a la última sugerencia: ** por favor ** use mayúsculas para los identificadores de tipo. Con muchas fuentes, distinguir entre '1ll' y' 111' puede ser difícil, si no imposible; '1LL' es claro y no ambiguo (y no hay sufijo' O' para crear problemas con '0'). –

+0

Sugerencia tomada. :) – Mysticial

5

La razón es porque falla 1 << 321 no tiene el tipo correcto (es int).El compilador no realiza ninguna conversión mágica antes de que la asignación en sí suceda realmente, por lo que 1 << 32 se evalúa usando int arithmic, dando una advertencia sobre un desbordamiento.

Intente utilizar 1LL o 1ULL en su lugar, que respectivamente tienen el tipo long long y unsigned long long.

3

La línea

unsigned long long n = 1<<32; 

resultados en un desbordamiento, debido a que el literal 1 es de tipo int, por lo 1 << 32 es también un int, que es de 32 bits en la mayoría de los casos.

La línea

unsigned long long n = 1<<31; 

desborda también, por la misma razón. Tenga en cuenta que 1 es del tipo signed int, por lo que realmente solo tiene 31 bits para el valor y 1 bit para el signo. Por lo tanto, cuando cambia 1 << 31, desborda los bits de valor, lo que resulta en -2147483648, que luego se convierte en un largo sin signo, que es 18446744071562067968. Puede verificar esto en el depurador, si inspecciona las variables y las convierte.

a fin de utilizar

unsigned long long n = 1ULL << 31; 
Cuestiones relacionadas