tendencias responder populares como c++ c++11 types type-conversion integer-promotion

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í:

¿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 (como int ). En particular, los operadores aritméticos no aceptan tipos más pequeños que int 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 o wchar_t cuyo rango de conversión de entero (4.13) es menor que el rango de int puede convertirse en un prvalue de tipo int si int puede representar todos los valores de la fuente tipo; de lo contrario, el prvalue de origen se puede convertir en un prvalue de tipo unsigned 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 .