c++ multiplication uint64

c++ - Curioso error aritmético: 255x256x256x256=18446744073692774400



multiplication uint64 (5)

Encontré algo extraño cuando estaba programando bajo 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

mientras que debería ser: (utilizando calculadora confirma)

4278190080

¿Alguien puede decirme cómo podría ser posible?


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


La respuesta es simple - desbordada.


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

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

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

En primer lugar, veamos la multiplicación. Estoy escribiendo el lado derecho en hexadecimal porque es más fácil ver lo que sucede de esa manera.

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

El último resultado, 0xFF000000u , tiene el bit más alto de un conjunto de números de 32 bits. La conversión de ese valor a un tipo de 32 bits con signo hace que se convierta en negativo como si se hubiera sustraído de él (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 conviertes en un tipo firmado; Sólo se reinterpreta.

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

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

Y si escribes 0xFFFFFFFFFF000000 en decimal, obtienes 18446744073692774400.

Como consejo final, siempre que obtenga un número entero "imposible" de C o C ++, intente imprimirlo en hexadecimal; Es mucho más fácil ver las rarezas de la aritmética de ancho fijo de dos complementos de esa manera.


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 a un int sin firmar de 64 bits, es el valor que observa.


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 de 32-bit . 255 x (2^24) es 4278190080 que no se puede representar en un int 32-bit (el valor máximo suele ser 2147483647 en un int 32-bit en la representación de complemento de dos).

C y C ++ 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í ^ es el operador matemático de potencia .