Règles de conversion de type implicites dans les opérateurs C ++

167

Je veux mieux savoir quand je devrais lancer. Quelles sont les règles de conversion de type implicite en C ++ lors de l'ajout, de la multiplication, etc. Par exemple,

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

etc...

L'expression sera-t-elle toujours évaluée comme le type le plus précis? Les règles diffèrent-elles pour Java? Veuillez me corriger si j'ai formulé cette question de manière inexacte.

Matt Montag
la source
16
Gardez à l'esprit que ^XOR.
GManNickG
16
@int ^ float = erreur de compilation :)
Serge Dundich

Réponses:

223

En C ++, les opérateurs (pour les types POD) agissent toujours sur les objets du même type.
Ainsi, s'ils ne sont pas identiques, l'un sera promu pour correspondre à l'autre.
Le type du résultat de l'opération est le même que les opérandes (après conversion).

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

Remarque. La taille minimale des opérations est de int. Donc short/ charsont promus intavant que l'opération ne soit effectuée.

Dans toutes vos expressions, le intest promu en a floatavant que l'opération ne soit effectuée. Le résultat de l'opération est un 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
la source
1
"La taille minimale des opérations est int." - Ce serait très étrange (qu'en est-il des architectures qui supportent efficacement les opérations char / short?) Est-ce vraiment dans la spécification C ++?
Rafał Dowgird
3
@Rafal: Oui. int est censé être le type entier le plus efficace pour une opération sur une plate-forme particulière. char doit toujours être 1 mais short peut avoir la même taille que int.
Martin York
1
@ Rafał: oui, c'est très étrange et c'est dans la norme. Dans de nombreux cas, l'architecture que vous décrivez pourrait utiliser son type super efficace char. Si la valeur de char + charest affectée à a char, alors il peut simplement effectuer l'arithmétique charet, par exemple, faire un tour. Mais si le résultat est assigné, intil doit faire l'arithmétique dans un type suffisamment grand pour obtenir le résultat correct quand il est supérieur à CHAR_MAX.
Steve Jessop
2
Je veux juste souligner le fait que int est promu int non signé !!! J'ai lutté contre des bugs pendant des jours parce que j'avais l'impression que les deux seraient promus à int ou long afin qu'un éventuel résultat négatif ne provoque pas de sous-flux / enveloppement.
nitsas
10
Exemple du problème " int est promu en unsigned int ": ((int) 4) - ((unsigned int) 5)se traduira 4294967295par des entiers 32 bits et des entiers non signés 32 bits.
nitsas
33

Opérations arithmétiques impliquant des floatrésultats dans float.

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

Pour une réponse plus détaillée. Regardez ce que dit la section §5 / 9 du standard C ++

De nombreux opérateurs binaires qui attendent des opérandes de type arithmétique ou énumération provoquent des conversions et produisent des types de résultats de la même manière. Le but est de produire un type commun, qui est également le type du résultat .

Ce modèle est appelé les conversions arithmétiques habituelles, qui sont définies comme suit:

- Si l'un des opérandes est de type double long, l'autre doit être converti en double long.

- Sinon, si l'un des opérandes est double, l'autre doit être converti en double.

- Sinon, si l'un des opérandes est float, l'autre doit être converti en float.

- Sinon, les promotions intégrales (4.5) doivent être effectuées sur les deux opérandes.54)

- Ensuite, si l'un des opérandes est long non signé, l'autre doit être converti en non signé long.

- Sinon, si un opérande est un long int et l'autre non signé, alors si un long int peut représenter toutes les valeurs d'un unsigned int, le unsigned int doit être converti en un long int; sinon, les deux opérandes doivent être convertis en un entier long non signé.

- Sinon, si l'un des opérandes est long, l'autre doit être converti en long.

- Sinon, si l'un des opérandes n'est pas signé, l'autre doit être converti en non signé.

[Remarque: sinon, le seul cas restant est que les deux opérandes sont des entiers]

Nawaz
la source
3
... tant que l'autre type n'est ni doubleni long double.
CB Bailey
1
@Charles: C'est exact. J'ai cité la section pertinente de la norme pour clarifier davantage.
Nawaz
Un entier peut-il donc toujours être converti en flottant sans aucune perte de données? (par exemple en mettant à zéro l'exposant et en utilisant tout pour la mantisse)?
Marco A.
1
Cette réponse est périmée. Suggérer une mise à jour. En particulier, long longet unsigned longnon abordé ici.
chux
@MarcoA. un 32 bits floatn'a pas assez de bits dans la mantisse (24 bits pour IEEE-754 ) pour un 32 bits int, il peut donc y avoir une perte de données. Un 64 bits doubledevrait être très bien.
Mark Ransom le
17

Puisque les autres réponses ne parlent pas des règles en C ++ 11, en voici une. De la norme C ++ 11 (projet n3337) §5 / 9 (a souligné la différence):

Ce modèle est appelé les conversions arithmétiques habituelles , qui sont définies comme suit:

- Si l'un des opérandes est de type énumération de portée, aucune conversion n'est effectuée; si l'autre opérande n'a pas le même type, l'expression est mal formée.

- Si l'un des opérandes est de type double long, l'autre doit être converti en double long.

- Sinon, si l'un des opérandes est double, l'autre doit être converti en double.

- Sinon, si l'un des opérandes est float, l'autre doit être converti en float.

- Sinon, les promotions intégrales seront effectuées sur les deux opérandes. Ensuite, les règles suivantes doivent être appliquées aux opérandes promus:

- Si les deux opérandes ont le même type, aucune conversion supplémentaire n'est nécessaire.

- Sinon, si les deux opérandes ont des types entiers signés ou les deux ont des types entiers non signés, l'opérande avec le type de rang de conversion d'entier inférieur doit être converti en le type de l'opérande de rang supérieur.

- Sinon, si l'opérande qui a un type entier non signé a un rang supérieur ou égal au rang du type de l'autre opérande, l'opérande de type entier signé doit être converti en type de l'opérande de type entier non signé.

- Sinon, si le type de l'opérande de type entier signé peut représenter toutes les valeurs du type de l'opérande de type entier non signé, l'opérande de type entier non signé doit être converti en type de l'opérande de type entier signé.

- Sinon, les deux opérandes doivent être convertis en type entier non signé correspondant au type de l'opérande de type entier signé.

Voir ici pour une liste fréquemment mise à jour.

légendes2k
la source
1
Ces règles étaient les mêmes dans toutes les versions de C ++, à l'exception des énumérations de portée qui ont été ajoutées dans C ++ 11 bien sûr
MM
6

Cette réponse est en grande partie dirigée vers un commentaire de @ RafałDowgird:

"La taille minimale des opérations est int." - Ce serait très étrange (qu'en est-il des architectures qui supportent efficacement les opérations char / short?) Est-ce vraiment dans la spécification C ++?

Gardez à l'esprit que la norme C ++ a la règle primordiale «comme si». Voir section 1.8: Exécution du programme:

3) Cette disposition est parfois appelée la règle du "comme si", car une mise en œuvre est libre de ne pas tenir compte de toute exigence de la Norme tant que le résultat est comme si l'exigence avait été respectée, dans la mesure où il peut être déterminé à partir de l'observable comportement du programme.

Le compilateur ne peut pas définir une inttaille de 8 bits, même si elle était la plus rapide, car la norme impose un minimum de 16 bits int.

Par conséquent, dans le cas d'un ordinateur théorique avec des opérations 8 bits ultra-rapides, la promotion implicite vers intpour l'arithmétique pourrait avoir une importance. Cependant, pour de nombreuses opérations, vous ne pouvez pas dire si le compilateur a réellement effectué les opérations avec la précision de an intpuis les a converties en a charpour les stocker dans votre variable, ou si les opérations ont été effectuées dans char tout au long.

Par exemple, considérez unsigned char = unsigned char + unsigned char + unsigned char, où l'addition déborderait (supposons une valeur de 200 pour chacun). Si vous êtes promu int, vous obtiendrez 600, qui seraient alors implicitement convertis en un unsigned char, qui encapsulerait le modulo 256, donnant ainsi un résultat final de 88. Si vous ne faisiez pas de telles promotions, vous devrez encapsuler entre les premières deux ajouts, ce qui réduirait le problème de 200 + 200 + 200à 144 + 200, qui est 344, qui se réduit à 88. En d'autres termes, le programme ne connaît pas la différence, donc le compilateur est libre d'ignorer le mandat pour effectuer des opérations intermédiaires intsi les opérandes ont un classement inférieur à int.

Cela est vrai en général pour l'addition, la soustraction et la multiplication. Ce n'est pas vrai en général pour la division ou le module.

David Stone
la source
4

Si vous excluez les types non signés, il existe une hiérarchie ordonnée: signé char, short, int, long, long long, float, double, long double. Tout d'abord, tout ce qui précède int dans ce qui précède sera converti en int. Ensuite, dans une opération binaire, le type classé le plus bas sera converti en type le plus élevé et les résultats seront le type du type le plus élevé. (Vous noterez que, à partir de la hiérarchie, chaque fois qu'un type virgule flottante et un type intégral sont impliqués, le type intégral sera converti en type virgule flottante.)

Le non signé complique un peu les choses: cela perturbe le classement, et des parties du classement deviennent implémentées. Pour cette raison, il est préférable de ne pas mélanger signé et non signé dans la même expression. (La plupart des experts C ++ semblent éviter les non-signés, sauf si des opérations au niveau du bit sont impliquées. C'est du moins ce que recommande Stroustrup.)

James Kanze
la source
3
Stroustrup peut recommander ce qu'il aime, mais utiliser une signature intpour un nombre qui n'a jamais besoin d'être négatif est un gaspillage complet de 50% de la plage disponible. Je ne suis certainement pas Stroustrup, mais je l'utilise unsignedpar défaut et signeduniquement lorsque j'ai une raison.
underscore_d
1
C'est bien beau, underscore_d, jusqu'au jour où vous devez soustraire. Le principal problème avec les nombres non signés en C ++ est que lorsque vous effectuez une soustraction, ils restent non signés. Supposons donc que vous écriviez une fonction pour voir si un std :: vector est en ordre. Vous pourriez écrire bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;et vous seriez ennuyé de constater qu'il se bloque pour les vecteurs vides parce que size () - 1 renvoie 18446744073709551615.
jorgbrown
3

Ma solution au problème a obtenu WA (mauvaise réponse), puis j'ai changé l'un des inten long long intet cela a donné AC (accepter) . Auparavant, j'essayais de le faire long long int += int * int, et après je l'ai rectifié long long int += long long int * int. J'ai trouvé sur Google,

1. Conversions arithmétiques

Conditions de conversion de type:

Conditions remplies ---> Conversion

  • Les deux opérandes sont de type double long . ---> L'autre opérande est converti en type long double .

  • La condition précédente n'est pas remplie et l'un ou l'autre des opérandes est de type double . ---> L'autre opérande est converti en type double .

  • Les conditions précédentes ne sont pas remplies et l'un ou l'autre des opérandes est de type float . ---> L'autre opérande est converti en type float .

  • Conditions précédentes non remplies (aucun des opérandes n'est de type flottant). ---> Les promotions intégrales sont effectuées sur les opérandes comme suit:

    • Si l'un des opérandes est de type unsigned long , l'autre opérande est converti en type unsigned long .
    • Si la condition précédente n'est pas remplie et si l'un des opérandes est de type long et l'autre de type unsigned int , les deux opérandes sont convertis en type unsigned long .
    • Si les deux conditions précédentes ne sont pas remplies et si l'un des opérandes est de type long , l'autre opérande est converti en type long .
    • Si les trois conditions précédentes ne sont pas remplies et si l'un des opérandes est de type unsigned int , l'autre opérande est converti en type unsigned int .
    • Si aucune des conditions précédentes n'est remplie, les deux opérandes sont convertis en type int .

2. Règles de conversion d'entiers

  • Promotions entières:

Les types entiers plus petits que int sont promus lorsqu'une opération est effectuée sur eux. Si toutes les valeurs du type d'origine peuvent être représentées sous la forme d'un int, la valeur du type le plus petit est convertie en un int; sinon, il est converti en un entier non signé. Les promotions entières sont appliquées dans le cadre des conversions arithmétiques habituelles de certaines expressions d'argument; opérandes des opérateurs unaires +, - et ~; et les opérandes des opérateurs de décalage.

  • Rang de conversion entier:

    • Deux types d'entiers signés ne doivent pas avoir le même rang, même s'ils ont la même représentation.
    • Le rang d'un type entier signé doit être supérieur au rang de tout type d'entier signé avec moins de précision.
    • Le grade de long long intsera supérieur au grade de long int, qui sera supérieur au grade de int, qui sera supérieur au grade de short int, qui sera supérieur au grade de signed char.
    • Le rang de tout type entier non signé doit être égal au rang du type entier signé correspondant, le cas échéant.
    • Le rang de tout type d'entier standard doit être supérieur au rang de tout type d'entier étendu de même largeur.
    • Le rang de charsera égal au rang de signed charet unsigned char.
    • Le rang de tout type d'entier signé étendu par rapport à un autre type d'entier signé étendu avec la même précision est défini par l'implémentation mais toujours soumis aux autres règles de détermination du rang de conversion d'entier.
    • Pour tous les types d'entiers T1, T2 et T3, si T1 a un rang supérieur à T2 et T2 a un rang supérieur à T3, alors T1 a un rang supérieur à T3.
  • Conversions arithmétiques usuelles:

    • Si les deux opérandes ont le même type, aucune conversion supplémentaire n'est nécessaire.
    • Si les deux opérandes sont du même type entier (signé ou non signé), l'opérande avec le type de rang de conversion d'entier inférieur est converti en type de l'opérande de rang supérieur.
    • Si l'opérande qui a un type entier non signé a un rang supérieur ou égal au rang du type de l'autre opérande, l'opérande avec le type entier signé est converti en type de l'opérande avec le type entier non signé.
    • Si le type de l'opérande avec le type entier signé peut représenter toutes les valeurs du type de l'opérande avec le type entier non signé, l'opérande avec le type entier non signé est converti en type de l'opérande avec le type entier signé.
    • Sinon, les deux opérandes sont convertis au type entier non signé correspondant au type de l'opérande avec le type entier signé. Des opérations spécifiques peuvent compléter ou modifier la sémantique des opérations arithmétiques habituelles.
garakchy
la source
1

Tout le chapitre 4 parle des conversions, mais je pense que vous devriez surtout vous intéresser à celles-ci:

4.5 Promotions intégrales [conv.prom]
Une rvalue de type char, signed char, unsigned char, short int ou unsigned short int peut être convertie en une rvalue de type int si int peut représenter toutes les valeurs du type source; sinon
, la rvalue source peut être convertie en une rvalue de type unsigned int.
Une rvalue de type wchar_t (3.9.1) ou un type d'énumération (7.2) peut être convertie en une rvalue du premier
des types suivants qui peut représenter toutes les valeurs de son type sous-jacent: int, unsigned int,
long ou unsigned longue.
Une rvalue pour un champ binaire intégral (9.6) peut être convertie en une rvalue de type int si int peut représenter toutes
les valeurs du champ binaire; sinon, il peut être converti en unsigned int si unsigned int peut représenter
renvoyer toutes les valeurs du champ de bits. Si le champ de bits est encore plus grand, aucune promotion intégrale ne s'applique à lui. Si le
champ de bits a un type énuméré, il est traité comme toute autre valeur de ce type à des fins de promotion.
Une rvalue de type bool peut être convertie en une rvalue de type int, avec false devenant zéro et true
devenant un.
Ces conversions sont appelées promotions intégrales.

4.6 Promotion en virgule flottante [conv.fpprom]
Une rvalue de type float peut être convertie en une rvalue de type double. La valeur est inchangée.
Cette conversion est appelée promotion en virgule flottante.

Par conséquent, toutes les conversions impliquant float - le résultat est float.

Uniquement celui impliquant les deux int - le résultat est int: int / int = int

BЈовић
la source
1

Le type de l'expression, lorsque les deux parties ne sont pas du même type, sera converti en la plus grande des deux. Le problème ici est de comprendre lequel est plus grand que l'autre (cela n'a rien à voir avec la taille en octets).

Dans les expressions dans lesquelles un nombre réel et un nombre entier sont impliqués, l'entier sera promu en nombre réel. Par exemple, dans int + float, le type de l'expression est float.

L'autre différence est liée à la capacité du type. Par exemple, une expression impliquant un int et un long int sera de type long int.

Baltasarq
la source
2
Ce n'est pas vrai. Sur les plates-formes de mai, a longest "plus grand" que a floatmais quel est le type de long+ float?
CB Bailey
1
-1: Qu'entendez-vous par plus grand ? Un float est-il plus grand qu'un int? Ou vice versa ?
Paul R
2
Merci pour vos commentaires. Oui, la taille en octets ici n'a aucun intérêt. Au fur et à mesure, il ne suffit pas de mettre le plus gros en italique pour expliquer la réponse. Quoi qu'il en soit, cela n'a pas de sens de l'expliquer plus profondément, car il existe maintenant d'autres réponses très approfondies.
Baltasarq
-2

Caveat!

Les conversions se produisent de gauche à droite.

Essaye ça:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0
Habib
la source
9
Ce n'est pas à cause de la conversion, mais à cause de la priorité des opérateurs. j + i * kdonnerait 101.
gartenriese