sirven que programas programación programa para librerias ingenieros funciones ejemplos comandos clases basicos c++ type-conversion unsigned-integer arithmetic-expressions

que - Conversión implícita de C++(firmado+sin firmar)



programa c++ (3)

Entiendo que, con respecto a las conversiones implícitas, si tenemos un operando de tipo sin firmar y un operando de tipo firmado, y el tipo del operando sin firmar es el mismo (o mayor) que el tipo del operando con signo, el operando con signo se convertirá a unsigned.

Asi que:

unsigned int u = 10; signed int s = -8; std::cout << s + u << std::endl; //prints 2 because it will convert `s` to `unsigned int`, now `s` has the value //4294967288, then it will add `u` to it, which is an out-of-range value, so, //in my machine, `4294967298 % 4294967296 = 2`

Lo que no entiendo - He leído que si el operando con signo tiene un tipo más grande que el operando sin firmar:

  • si todos los valores en el tipo sin firmar se ajustan al tipo más grande, entonces el operando sin firmar se convierte al tipo firmado

  • si los valores en el tipo sin firmar no caben en el tipo más grande, entonces el operando con signo se convertirá al tipo sin signo

así que en el siguiente código:

signed long long s = -8; unsigned int u = 10; std::cout << s + u << std::endl;

¿Se convertirá a larga firmada porque los valores int pueden caber en largo firmado?

Si ese es el caso, ¿en qué escenario los valores de tipo más pequeños no caben en el más grande?


signed int no cabe en unsigned long long . Por lo tanto, tendrá esta conversión: signed int -> unsigned long long .


Cita relevante de la Norma:

5 expresiones [expr]

10 Muchos operadores binarios que esperan operandos de tipo aritmético o de enumeración causan conversiones y producen tipos de resultados de forma similar. El propósito es producir un tipo común, que también es el tipo del resultado. Este patrón se denomina conversiones aritméticas habituales, que se definen de la siguiente manera:

[2 cláusulas sobre tipos iguales o tipos de igual signo omitidos]

- De lo contrario, si el operando que tiene un tipo entero sin signo tiene un rango mayor o igual que el rango del tipo del otro operando, el operando con tipo entero con signo se convertirá al tipo del operando con tipo entero sin signo.

- De lo contrario, si el tipo del operando con tipo entero con signo puede representar todos los valores del tipo del operando con tipo entero sin signo, el operando con tipo entero sin signo se convertirá al tipo del operando con tipo entero con signo.

- De lo contrario, ambos operandos se convertirán al tipo de entero sin signo correspondiente al tipo del operando con tipo entero con signo.

Consideremos los siguientes 3 ejemplos de casos para cada una de las 3 cláusulas anteriores en un sistema donde sizeof(int) < sizeof(long) == sizeof(long long) (fácilmente adaptable a otros casos)

#include <iostream> signed int s1 = -4; unsigned int u1 = 2; signed long int s2 = -4; unsigned int u2 = 2; signed long long int s3 = -4; unsigned long int u3 = 2; int main() { std::cout << (s1 + u1) << "/n"; // 4294967294 std::cout << (s2 + u2) << "/n"; // -2 std::cout << (s3 + u3) << "/n"; // 18446744073709551614 }

Ejemplo en vivo con salida.

Primera cláusula: tipos de rango igual, por lo que el operando signed int se convierte a unsigned int . Esto implica una transformación de valor que (usando el complemento de dos) le da al valor impreso.

Segunda cláusula: el tipo con signo tiene mayor rango, y (¡en esta plataforma!) Puede representar todos los valores del tipo sin signo, por lo que el operando sin signo se convierte en tipo con signo, y obtienes -2

Tercera cláusula: el tipo firmado nuevamente tiene un rango más alto, pero (¡en esta plataforma!) No puede representar todos los valores del tipo sin signo, por lo que ambos se convierten en unsigned long long y después de la transformación del valor en el operando firmado, se obtiene valor impreso.

Tenga en cuenta que cuando el operando sin firmar sea lo suficientemente grande (por ejemplo, 6 en estos ejemplos), el resultado final daría 2 para los 3 ejemplos debido al desbordamiento del entero sin signo.

(Agregado) Tenga en cuenta que obtendrá aún más resultados inesperados cuando haga comparaciones en estos tipos. Consideremos el ejemplo 1 anterior con < :

#include <iostream> signed int s1 = -4; unsigned int u1 = 2; int main() { std::cout << (s1 < u1 ? "s1 < u1" : "s1 !< u1") << "/n"; // "s1 !< u1" std::cout << (-4 < 2u ? "-4 < 2u" : "-4 !< 2u") << "/n"; // "-4 !< 2u" }

Como 2u se hace unsigned explícitamente por el sufijo u , se aplican las mismas reglas. Y el resultado probablemente no sea el que espera al comparar -4 <2 al escribir en C ++ -4 < 2u ...


Tenga en cuenta que el estándar C ++ 11 no habla sobre los tipos más grandes o más pequeños aquí, sino sobre los tipos con rango inferior o superior.

Considere el caso de long int y unsigned int donde ambos son de 32 bits. El long int tiene un rango más grande que el unsigned int , pero como long int y unsigned int son ambos de 32 bits, long int no puede representar todos los valores de unsigned int .

Por lo tanto, caemos en el último caso (C ++ 11: 5.6p9):

  • De lo contrario, ambos operandos se convertirán al tipo de entero sin signo correspondiente al tipo del operando con tipo entero con signo.

Esto significa que tanto el long int como el unsigned int se convertirán a unsigned long int .