Reglas de conversión de tipo implícito en operadores C ++

167

Quiero ser mejor sobre saber cuándo debo lanzar. ¿Cuáles son las reglas de conversión de tipo implícito en C ++ al agregar, multiplicar, etc. Por ejemplo,

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

etcétera...

¿Se evaluará siempre la expresión como el tipo más preciso? ¿Las reglas difieren para Java? Corríjame si he formulado esta pregunta de manera incorrecta.

Matt Montag
fuente
16
Tenga en cuenta que ^es XOR.
GManNickG
16
@int ^ float = error de compilación :)
Serge Dundich

Respuestas:

223

En C ++, los operadores (para los tipos de POD) siempre actúan sobre objetos del mismo tipo.
Por lo tanto, si no son iguales, se promoverá uno para que coincida con el otro.
El tipo del resultado de la operación es el mismo que el de los operandos (después de la conversión).

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

Nota. El tamaño mínimo de las operaciones es int. Entonces short/ charson promovidos a intantes de que se realice la operación.

En todas sus expresiones, intse promueve a floatantes de que se realice la operación. El resultado de la operación es a float.

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>
Martin York
fuente
1
"El tamaño mínimo de las operaciones es int". - Esto sería muy extraño (¿qué pasa con las arquitecturas que soportan eficientemente las operaciones char / short?) ¿Está esto realmente en la especificación de C ++?
Rafał Dowgird
3
@ Rafal: Sí. Se supone que int es el tipo de entero más eficiente para operar en una plataforma en particular. char siempre debe ser 1 pero short puede ser del mismo tamaño que int.
Martin York
1
@ Rafał: sí, es muy extraño y está en el estándar. En muchos casos, la arquitectura que describe podría usar su tipo súper eficiente char. Si el valor de char + charse asigna a a char, entonces solo puede hacer la aritmética chary, por ejemplo, ajustar. Pero si se asigna el resultado, intentonces tiene que hacer la aritmética en un tipo lo suficientemente grande como para obtener el resultado correcto cuando es más que CHAR_MAX.
Steve Jessop
2
¡Solo quiero enfatizar el hecho de que int es promovido a unsigned int ! He luchado con los errores durante días porque tenía la impresión de que ambos serían promovidos a int o long para que un posible resultado negativo no causara un desbordamiento / ajuste.
nitsas
10
Ejemplo del problema " int es promovido a unsigned int ": ((int) 4) - ((unsigned int) 5)dará como resultado 4294967295entradas de 32 bits y entradas de 32 bits sin signo.
nitsas
33

Operaciones aritméticas que implican floatresultados en float.

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

Para más detalles responda. Mire lo que dice la sección §5 / 9 del Estándar C ++

Muchos operadores binarios que esperan operandos de tipo aritmético o de enumeración causan conversiones y producen tipos de resultados de manera similar. El propósito es producir un tipo común, que también es el tipo del resultado .

Este patrón se llama las conversiones aritméticas habituales, que se definen de la siguiente manera:

- Si cualquiera de los operandos es del tipo long double, el otro se convertirá en long double.

- De lo contrario, si cualquiera de los operandos es doble, el otro se convertirá en doble.

- De lo contrario, si cualquiera de los operandos es flotante, el otro se convertirá en flotante.

- De lo contrario, las promociones integrales (4.5) se realizarán en ambos operandos.

- Entonces, si cualquiera de los operandos no tiene signo largo, el otro se convertirá a largo sin signo.

- De lo contrario, si un operando es un int largo y el otro unsigned int, entonces si un int largo puede representar todos los valores de un unsigned int, el unsigned int se convertirá en un int largo; de lo contrario, ambos operandos se convertirán a int sin signo.

- De lo contrario, si cualquiera de los operandos es largo, el otro se convertirá en largo.

- De lo contrario, si cualquiera de los operandos no tiene signo, el otro se convertirá en sin signo.

[Nota: de lo contrario, el único caso restante es que ambos operandos son int]

Nawaz
fuente
3
... siempre que el otro tipo no sea ni doubletampoco long double.
CB Bailey
1
@ Charles: Correcto. Cité la sección correspondiente de la Norma para aclarar más.
Nawaz
Entonces, ¿puede un entero convertirse siempre en flotante sin pérdida de datos? (por ejemplo, poniendo a cero el exponente y usando todo para la mantisa)?
Marco A.
1
Esta respuesta está desactualizada. Sugerir actualización En particular, long longy unsigned longno se aborda aquí.
chux - Restablece a Mónica el
@MarcoA. un 32 bits floatno tiene suficientes bits en la mantisa (24 bits para IEEE-754 ) para un 32 bits int, por lo que puede haber alguna pérdida de datos. Un 64 bits doubledebería estar bien.
Mark Ransom
17

Como las otras respuestas no hablan sobre las reglas en C ++ 11, aquí hay una. Del estándar C ++ 11 (borrador n3337) §5 / 9 (enfatizó la diferencia):

Este patrón se llama las conversiones aritméticas habituales , que se definen de la siguiente manera:

- Si cualquiera de los operandos es del tipo de enumeración con ámbito, no se realizan conversiones; Si el otro operando no tiene el mismo tipo, la expresión está mal formada.

- Si cualquiera de los operandos es del tipo long double, el otro se convertirá en long double.

- De lo contrario, si cualquiera de los operandos es doble, el otro se convertirá en doble.

- De lo contrario, si cualquiera de los operandos es flotante, el otro se convertirá en flotante.

- De lo contrario, las promociones integrales se realizarán en ambos operandos. Luego se aplicarán las siguientes reglas a los operandos promocionados:

- Si ambos operandos tienen el mismo tipo, no se necesita más conversión.

- De lo contrario, si ambos operandos tienen tipos enteros con signo o ambos tienen tipos enteros sin signo, el operando con el tipo de rango de conversión de entero menor se convertirá al tipo del operando con rango mayor.

- 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 de operando con tipo entero con signo puede representar todos los valores del tipo de operando con tipo entero sin signo, el operando con tipo entero sin signo se convertirá en el tipo de operando con tipo entero con signo.

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

Vea aquí una lista que se actualiza con frecuencia.

legends2k
fuente
1
Estas reglas fueron las mismas en todas las versiones de C ++, excepto en las enumeraciones de alcance que se agregaron en C ++ 11, por supuesto
MM
6

Esta respuesta está dirigida en gran parte a un comentario hecho por @ RafałDowgird:

"El tamaño mínimo de las operaciones es int". - Esto sería muy extraño (¿qué pasa con las arquitecturas que soportan eficientemente las operaciones char / short?) ¿Está esto realmente en la especificación de C ++?

Tenga en cuenta que el estándar C ++ tiene la importante regla "como si". Ver sección 1.8: Ejecución del programa:

3) Esta disposición a veces se llama la regla "como si", porque una implementación es libre de ignorar cualquier requisito de la Norma siempre que el resultado sea como si el requisito hubiera sido obedecido, en la medida en que se pueda determinar a partir de lo observable comportamiento del programa.

El compilador no puede establecer un inttamaño de 8 bits, incluso si fuera el más rápido, ya que el estándar exige un mínimo de 16 bits int.

Por lo tanto, en el caso de una computadora teórica con operaciones súper rápidas de 8 bits, la promoción implícita intde aritmética podría ser importante. Sin embargo, para muchas operaciones, no puede saber si el compilador realmente realizó las operaciones con la precisión de an inty luego se convirtió en a charpara almacenar en su variable, o si las operaciones se realizaron en char todo el tiempo.

Por ejemplo, considere unsigned char = unsigned char + unsigned char + unsigned chardónde se desbordaría la suma (supongamos un valor de 200 para cada uno). Si ascendieras a int, obtendrías 600, que luego se convertirían implícitamente en un unsigned char, que envolvería el módulo 256, dando así un resultado final de 88. Si no hicieras tales promociones, tendrías que envolver entre el primer dos adiciones, lo que reduciría el problema de 200 + 200 + 200a 144 + 200, que es 344, que se reduce a 88. En otras palabras, el programa no conoce la diferencia, por lo que el compilador es libre de ignorar el mandato de realizar operaciones intermedias intsi los operandos tienen una clasificación más baja que int.

Esto es cierto en general de suma, resta y multiplicación. No es cierto en general para la división o el módulo.

David Stone
fuente
4

Si excluye los tipos sin firmar, hay una jerarquía ordenada: char firmado, short, int, long, long long, float, double, long double. Primero, todo lo que venga antes de int en lo anterior se convertirá a int. Luego, en una operación binaria, el tipo de clasificación inferior se convertirá en el superior y los resultados serán del tipo superior. (Notará que, desde la jerarquía, cada vez que intervienen un punto flotante y un tipo integral, el tipo integral se convertirá al tipo de punto flotante).

Unsigned complica un poco las cosas: perturba la clasificación y partes de la clasificación se definen como implementación. Debido a esto, es mejor no mezclar con y sin signo en la misma expresión. (La mayoría de los expertos en C ++ parecen evitar sin firmar a menos que estén involucradas operaciones bit a bit. Al menos, eso es lo que recomienda Stroustrup).

James Kanze
fuente
3
Stroustrup puede recomendar lo que le gusta, pero usar un signo intpara un número que nunca necesita ser negativo es un desperdicio completo de un 50% del rango disponible. Ciertamente no soy Stroustrup, pero lo uso unsignedpor defecto y signedsolo cuando tengo una razón.
underscore_d
1
Eso está muy bien, subrayado_d, hasta el día en que tienes que restar. El problema principal con los números sin signo en C ++ es que cuando realiza la resta, permanecen sin signo. Supongamos que escribe una función para ver si un std :: vector está en orden. Puede escribir bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;y luego se molestaría al descubrir que se bloquea por vectores vacíos porque size () - 1 devuelve 18446744073709551615.
jorgbrown
3

Mi solución al problema tiene WA (respuesta incorrecta), entonces me cambió uno de inta long long inty se la dio CA (aceptar) . Anteriormente, estaba tratando de hacerlo long long int += int * int, y luego lo rectifico long long int += long long int * int. Google se me ocurrió,

1. Conversiones aritméticas

Condiciones para la conversión de tipo:

Condiciones cumplidas ---> Conversión

  • Cualquiera de los operandos es de tipo largo doble . ---> Otro operando se convierte a tipo largo doble .

  • Condición anterior no cumplida y cualquiera de los operandos es de tipo doble . ---> Otro operando se convierte al tipo double .

  • No se cumplen las condiciones anteriores y cualquiera de los operandos es de tipo flotante . ---> Otro operando se convierte a tipo flotante .

  • Condiciones anteriores no cumplidas (ninguno de los operandos son de tipo flotante). ---> Las promociones integrales se realizan en los operandos de la siguiente manera:

    • Si cualquiera de los operandos es de tipo sin signo largo , el otro operando se convierte en tipo sin signo de largo .
    • Si no se cumple la condición anterior, y si cualquiera de los operandos es de tipo largo y el otro de tipo unsigned int , ambos operandos se convierten a tipo unsigned long .
    • Si no se cumplen las dos condiciones anteriores, y si cualquiera de los operandos es de tipo largo , el otro operando se convierte a tipo largo .
    • Si no se cumplen las tres condiciones anteriores, y si cualquiera de los operandos es de tipo unsigned int , el otro operando se convierte a tipo unsigned int .
    • Si no se cumple ninguna de las condiciones anteriores, ambos operandos se convierten al tipo int .

2) Reglas de conversión de enteros

  • Promociones de enteros:

Los tipos enteros más pequeños que int se promueven cuando se realiza una operación en ellos. Si todos los valores del tipo original se pueden representar como un int, el valor del tipo más pequeño se convierte en un int; de lo contrario, se convierte en un int sin signo. Las promociones de enteros se aplican como parte de las conversiones aritméticas habituales a ciertas expresiones de argumento; operandos de los operadores unarios +, - y ~; y operandos de los operadores de turno.

  • Rango de conversión de enteros:

    • No hay dos tipos enteros con signo que tengan el mismo rango, incluso si tienen la misma representación.
    • El rango de un tipo entero con signo será mayor que el rango de cualquier tipo entero con signo con menos precisión.
    • El rango de long long intserá mayor que el rango de long int, que será mayor que el rango de int, que será mayor que el rango de short int, que será mayor que el rango de signed char.
    • El rango de cualquier tipo entero sin signo será igual al rango del tipo entero con signo correspondiente, si lo hay.
    • El rango de cualquier tipo entero estándar será mayor que el rango de cualquier tipo entero extendido con el mismo ancho.
    • El rango de charserá igual al rango de signed chary unsigned char.
    • El rango de cualquier tipo de entero con signo extendido en relación con otro tipo de entero con signo extendido con la misma precisión está definido por la implementación pero aún está sujeto a las otras reglas para determinar el rango de conversión de enteros.
    • Para todos los tipos enteros T1, T2 y T3, si T1 tiene un rango mayor que T2 y T2 tiene un rango mayor que T3, entonces T1 tiene un rango mayor que T3.
  • Conversiones aritméticas habituales:

    • Si ambos operandos tienen el mismo tipo, no se necesita más conversión.
    • Si ambos operandos son del mismo tipo entero (con signo o sin signo), el operando con el tipo de rango de conversión de entero menor se convierte al tipo del operando con mayor rango.
    • 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 convierte en el tipo del operando con tipo entero sin signo.
    • Si el tipo de operando con tipo entero con signo puede representar todos los valores del tipo de operando con tipo entero sin signo, el operando con tipo entero sin signo se convierte en el tipo de operando con tipo entero con signo.
    • De lo contrario, ambos operandos se convierten al tipo entero sin signo correspondiente al tipo del operando con tipo entero con signo. Las operaciones específicas pueden agregar o modificar la semántica de las operaciones aritméticas habituales.
garakchy
fuente
1

Todo el capítulo 4 habla sobre las conversiones, pero creo que debería interesarle principalmente:

4.5 Promociones integrales [conv.prom]
Un valor r de tipo char, char firmado, unsigned char, short int o unsigned short int se puede convertir en un rvalue de tipo int si int puede representar todos los valores del tipo fuente; de lo
contrario, el valor r de origen se puede convertir a un valor r de tipo unsigned int.
Un valor r de tipo wchar_t (3.9.1) o un tipo de enumeración (7.2) se puede convertir en un valor r del primero
de los siguientes tipos que puede representar todos los valores de su tipo subyacente: int, unsigned int,
long o unsigned largo.
Un valor r para un campo de bits integral (9.6) se puede convertir en un valor r de tipo int si int puede representar todos
los valores del campo de bits; de lo contrario, se puede convertir a unsigned int si unsigned int puede repetir
reenviar todos los valores del campo de bits. Si el campo de bits es aún mayor, no se aplica ninguna promoción integral. Si el
campo de bits tiene un tipo enumerado, se trata como cualquier otro valor de ese tipo para fines de promoción.
Un valor de tipo bool se puede convertir en un valor de tipo int, con falso convirtiéndose en cero y verdadero
convirtiéndose en uno.
Estas conversiones se llaman promociones integrales.

4.6 Promoción de punto flotante [conv.fpprom]
Un valor de tipo flotante se puede convertir en un valor de tipo doble. El valor no ha cambiado.
Esta conversión se llama promoción de punto flotante.

Por lo tanto, todas las conversiones que involucran flotante - el resultado es flotante.

Solo el que involucra ambos int: el resultado es int: int / int = int

BЈовић
fuente
1

El tipo de la expresión, cuando no ambas partes son del mismo tipo, se convertirá en la mayor de ambas. El problema aquí es entender cuál es más grande que la otra (no tiene nada que ver con el tamaño en bytes).

En las expresiones en las que están involucrados un número real y un número entero, el número entero será promovido a número real. Por ejemplo, en int + float, el tipo de expresión es float.

La otra diferencia está relacionada con la capacidad del tipo. Por ejemplo, una expresión que involucra un int y un int largo resultará del tipo long int.

Baltasarq
fuente
2
Esto no es verdad. En las plataformas de mayo a longes "más grande" que a floatpero ¿cuál es el tipo de long+ float?
CB Bailey
1
-1: ¿Qué quieres decir con mayor ? ¿Es un flotador más grande que un int? O viceversa ?
Paul R
2
Gracias por tus comentarios. Sí, el tamaño en bytes aquí no tiene ningún interés. Como se desprende, obviamente poner las letras más grandes en cursiva no es suficiente para explicar la respuesta. De todos modos, no tiene sentido explicarlo más profundamente, ya que ahora hay otras respuestas muy completas.
Baltasarq
-2

¡Consideración!

Las conversiones ocurren de izquierda a derecha.

Prueba esto:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0
Habib
fuente
9
Eso no se debe a la conversión sino a la precedencia del operador. j + i * kdaría como resultado 101.
gartenriese