c++ - responder - hashtag twitter populares
¿Por qué el corto sin signo corto(multiplicado) sin firmar se convierte al int firmado? (6)
Como lo han señalado otras respuestas, esto sucede debido a las reglas de promoción de enteros.
La forma más sencilla de evitar la conversión de un tipo sin signo con un rango más pequeño que un tipo con signo con un rango más alto, es asegurarse de que la conversión se realice en un unsigned int
y no en int
.
Esto se hace multiplicando por el valor 1 que es de tipo unsigned int. Debido a que 1 es una identidad multiplicativa, el resultado se mantendrá sin cambios:
unsigned short c = t * 1U * t;
Primero se evalúan los operandos t y 1U. El operando izquierdo está firmado y tiene un rango más pequeño que el operando derecho sin firmar, por lo que se convierte al tipo del operando derecho. Luego, los operandos se multiplican y lo mismo sucede con el resultado y el operando derecho restante. El último párrafo de la Norma que se cita a continuación se utiliza para esta promoción.
De lo contrario, las promociones enteras se realizan en ambos operandos. Luego se aplican las siguientes reglas a los operandos promovidos:
-Si ambos operandos tienen el mismo tipo, entonces no se necesita más conversión.
-De lo contrario, si ambos operandos tienen tipos enteros con signo o si ambos tienen tipos enteros sin signo, el operando con el tipo de menor rango de conversión de enteros se convierte al tipo del operando con mayor rango.
-De lo contrario, si el operando que tiene tipo entero sin signo tiene un rango mayor o igual al rango del tipo del otro operando, entonces el operando con tipo entero con signo se convierte al tipo del operando con tipo entero sin signo.
Esta pregunta ya tiene una respuesta aquí:
- Reglas de conversión de tipo implícito en operadores de C ++ 9 respuestas
¿Por qué el unsigned short * unsigned short
convierte a int
en C ++ 11?
El int
es demasiado pequeño para manejar valores máximos como lo demuestra esta línea de código.
cout << USHRT_MAX * USHRT_MAX << endl;
desbordamientos en MinGW 4.9.2
-131071
porque ( source )
USHRT_MAX = 65535 (2 ^ 16-1) o mayor *
INT_MAX = 32767 (2 ^ 15-1) o mayor *
y (2^16-1)*(2^16-1) = ~2^32
.
¿Debería esperar algún problema con esta solución ?
unsigned u = static_cast<unsigned>(t*t);
Este programa
unsigned short t;
cout<<typeid(t).name()<<endl;
cout<<typeid(t*t).name()<<endl;
da salida
t
i
en
gcc version 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
gcc version 4.8.2 (GCC)
MinGW 4.9.2
con ambos
g++ p.cpp
g++ -std=c++11 p.cpp
lo que prueba que t*t
se convierte a int
en estos compiladores.
Recursos útiles:
Firmado para la conversión sin firmar en C: ¿es siempre seguro?
Multiplicación de enteros con y sin signo.
https://bytes.com/topic/c-sharp/answers/223883-multiplication-types-smaller-than-int-yields-int
http://www.cplusplus.com/reference/climits
http://en.cppreference.com/w/cpp/language/types
Edición: He demostrado el problema en la siguiente imagen.
Como lo señaló Paolo M en los comentarios, USHRT_MAX
tiene el tipo int
(esto se especifica en 5.2.4.2.1 / 1: todas estas macros tienen un tipo al menos tan grande como el int
).
Entonces USHRT_MAX * USHRT_MAX
ya es int
x int
, no se producen promociones.
Esto invoca un desbordamiento de enteros con signo en su sistema, causando un comportamiento indefinido.
Respecto a la solución propuesta:
unsigned u = static_cast<unsigned>(t*t);
Esto no ayuda porque t*t
causa un comportamiento indefinido debido al desbordamiento de enteros con signo. Como explican las otras respuestas, t
se promueve a int
antes de que ocurra la multiplicación, por razones históricas.
En su lugar podría utilizar:
auto u = static_cast<unsigned int>(t) * t;
que, después de la promoción de enteros , es un unsigned int
multiplicado por un int
; y luego, de acuerdo con el resto de las conversiones aritméticas habituales , el int
se promueve a un unsigned int
, y se produce una multiplicación modular bien definida.
Con reglas de promoción de enteros.
USHRT_MAX
valor USHRT_MAX
se promueve a int
. luego hacemos la multiplicación de 2 int (con posible desbordamiento).
Es posible que desee leer sobre conversiones implícitas , especialmente la sección sobre promociones numéricas donde dice
Los prvalores de tipos integrales pequeños (como
char)
se pueden convertir a prvalores de tipos integrales más grandes (comoint
). En particular, los operadores aritméticos no aceptan tipos más pequeños queint
como argumentos
Lo que dice lo anterior es que si usa algo más pequeño que int
(como unsigned short
) en una expresión que involucra operadores aritméticos (que por supuesto incluye la multiplicación), los valores se promoverán a int
.
Parece que nadie ha respondido esta parte de la pregunta todavía:
¿Debería esperar algún problema con esta solución?
u = static_cast<unsigned>(t*t);
Sí, hay un problema aquí: primero calcula t*t
y le permite desbordarse, luego convierte el resultado a unsigned
. El desbordamiento de enteros causa un comportamiento indefinido de acuerdo con el estándar de C ++ (aunque en la práctica siempre puede funcionar bien). La solución correcta es:
u = static_cast<unsigned>(t)*t;
Tenga en cuenta que la segunda t
se promueve a unsigned
antes de la multiplicación porque el primer operando unsigned
está unsigned
.
Son las conversiones aritméticas habituales en acción.
Comúnmente se llama promoción de argumentos, aunque el estándar usa ese término de una manera más restringida (el conflicto eterno entre los términos descriptivos razonables y los estándares).
C ++ 11 §5 / 9:” Muchos operadores binarios que esperan operandos de tipo aritmético o de enumeración provocan conversiones y producen tipos de resultados de manera similar. El propósito es generar un tipo común, que también es el tipo del resultado. Este patrón se llama las conversiones aritméticas habituales [...]
El párrafo continúa describiendo los detalles, que equivalen a conversiones en una escala de tipos más generales, hasta que todos los argumentos se puedan representar. El escalón más bajo en esta escalera es la promoción integral de ambos operandos de una operación binaria, por lo que al menos eso se realiza (pero la conversión puede comenzar en un peldaño más alto). Y la promoción integral comienza con esto:
C ++ 11 §4.5 / 1:" Un prvalue de un tipo entero distinto de
bool
,char16_t
,char32_t
owchar_t
cuyo rango de conversión de entero (4.13) es menor que el rango deint
puede convertirse en un prvalue de tipoint
siint
puede representar todos los valores de la fuente tipo; de lo contrario, el prvalue de origen se puede convertir en un prvalue de tipounsigned int
Fundamentalmente, se trata de tipos, no de expresiones aritméticas. En su caso, los argumentos del operador de multiplicación *
se convierten a int
. Luego, la multiplicación se realiza como una multiplicación int
, dando un resultado int
.