Curioso error aritmético-255x256x256x256 = 18446744073692774400


Me encontré con algo extraño cuando estaba programando en c++. Se trata de una simple multiplicación.

Código:

unsigned __int64 a1 = 255*256*256*256;
unsigned __int64 a2= 255 << 24; // same as the above

cerr()<<"a1 is:"<<a1;
cerr()<<"a2 is:"<<a2;

Curiosamente el resultado es:

a1 is: 18446744073692774400 
a2 is: 18446744073692774400 

Considerando que debería ser: (usando calculadora confirma)

4278190080

¿Puede alguien decirme cómo podría ser posible?

Author: Eric Leschinski, 2012-09-20

5 answers

 255*256*256*256

Todos los operandos son int estás desbordando int. El desbordamiento de un entero con signo es un comportamiento indefinido en C y C++.

EDITAR:

Tenga en cuenta que la expresión 255 << 24 en su segunda declaración también invoca un comportamiento indefinido si su tipo int es 32-bit. 255 x (2^24) es 4278190080 que no se puede representar en un 32-bit int (el valor máximo es normalmente 2147483647 en un 32-bit int en la representación del complemento de dos).

C y C++ ambos dicen para E1 << E2 que si E1 es de tipo con signo y positivo y que E1 x (2^E2) no se puede representar en el tipo de E1, el programa invoca un comportamiento indefinido. Aquí ^está el operador matemático power.

 39
Author: ouah,
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
2012-09-20 14:14:47

Tus literales son int. Esto significa que todas las operaciones se realizan realmente en int, y se desbordan rápidamente. Este valor desbordado, cuando se convierte en un int de 64 bits sin signo, es el valor que observa.

 17
Author: Puppy,
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
2012-09-20 13:50:21

Tal vez valga la pena explicar lo que sucedió para producir el número 18446744073692774400. Técnicamente hablando, las expresiones que escribiste desencadenan un "comportamiento indefinido" y por lo tanto el compilador podría haber producido cualquier cosa como resultado; sin embargo, suponiendo que int es un tipo de 32 bits, que casi siempre lo es hoy en día, obtendrás la misma respuesta" incorrecta " si escribes{[14]]}

uint64_t x = (int) (255u*256u*256u*256u);

Y esa expresión no desencadena un comportamiento indefinido. (La conversión de unsigned int a intimplica comportamiento definido por la implementación, pero como nadie ha producido una CPU de complemento o signo y magnitud en muchos años, todas las implementaciones que es probable que encuentre lo definen exactamente de la misma manera.) He escrito el elenco en estilo C porque todo lo que estoy diciendo aquí se aplica por igual a C y C++.

Primero, veamos la multiplicación. Estoy escribiendo el lado derecho en hexadecimal porque es más fácil ver lo que está pasando que manera.

255u * 256u               = 0x0000FF00u
255u * 256u * 256u        = 0x00FF0000u
255u * 256u * 256u * 256u = 0xFF000000u (= 4278190080)

Ese último resultado, 0xFF000000u, tiene el bit más alto de un conjunto de números de 32 bits. Por lo tanto, la conversión de ese valor a un tipo de 32 bits con signo hace que se convierta en negativo como si 232 se le había restado (esa es la operación definida por la implementación que mencioné anteriormente).

(int) (255u*256u*256u*256u) = 0xFF000000 = -16777216

Escribo el número hexadecimal allí, sin el sufijo u, para enfatizar que el patrón de bits del valor no cambia cuando lo convierte a un tipo con signo; sólo se reinterpreta.

Ahora, cuando asigna -16777216 a una variable uint64_t, se vuelve a convertir a unsigned as-if agregando 264. (A diferencia de la conversión sin signo a signo, esta semántica está prescrita por el estándar.) Esto hace cambiar el patrón de bits, estableciendo todos los 32 bits altos del número a 1 en lugar de 0 como se esperaba:

(uint64_t) (int) (255u*256u*256u*256u) = 0xFFFFFFFFFF000000u

Y si escribes 0xFFFFFFFFFF000000 en decimal, obtienes 18446744073692774400.

Como una pieza de cierre de consejo, cada vez que obtengas un entero "imposible" de C o C++, intenta imprimirlo en hexadecimal; es mucho más fácil ver rarezas de aritmética de dos complementos de ancho fijo de esa manera.

 16
Author: zwol,
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
2012-09-20 21:17:22

La respuesta es simple overfl desbordada.

 0
Author: Lingfeng Xiong,
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
2012-09-21 03:29:59

Aquí se produjo un desbordamiento en int y cuando lo asigna a int64 sin firmar, se convierte en a 18446744073692774400 en lugar de 4278190080

 0
Author: Rahul,
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
2012-09-21 05:19:32