181 votes

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

Je veux être meilleur pour savoir quand je dois lancer. Quelles sont les règles implicites de conversion de type en C++ lors de l'addition, de la multiplication, etc. Par exemple,

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

et cetera...

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

18 votes

Gardez à l'esprit ^ est XOR.

18 votes

@int ^ float = erreur de compilation :)

244voto

Loki Astari Points 116129

En C++, les opérateurs (pour les types POD) agissent toujours sur des objets de 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 celui des opérandes (après conversion).

if:
either is      long double       other is promoted >      long double
either is           double       other is promoted >           double
either is           float        other is promoted >           float
either is long long unsigned int other is promoted > long long unsigned int
either is long long          int other is promoted > long long          int
either is long      unsigned int other is promoted > long      unsigned int
either is long               int other is promoted > long               int
either is           unsigned int other is promoted >           unsigned int
either is                    int other is promoted >                    int

Otherwise:
both operands are promoted to int

Note. La taille minimale des opérations est de int . Donc short / char sont promus à int avant que l'opération ne soit effectuée.

Dans toutes vos expressions, le int est promu à un float avant 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>

1 votes

"La taille minimale des opérations est int." - Cela 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++ ?

0 votes

Plus précisément : les opérateurs intégrés agissent toujours sur les mêmes types. Les opérateurs définis par l'utilisateur peuvent mélanger des types arbitraires, tant qu'au moins un opérande est d'un type défini par l'utilisateur.

4 votes

@Rafal : Oui. int est censé être le type d'entier le plus efficace pour fonctionner sur une plateforme particulière. char doit toujours être égal à 1 mais short peut avoir la même taille que int.

35voto

Nawaz Points 148870

Opérations arithmétiques impliquant float résulte en 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 de la norme C++.

De nombreux opérateurs binaires qui attendent des opérandes de type arithmétique ou d'énumération provoquent des conversions et produisent des d'une manière similaire. Le site but est de produire un type commun, qui est aussi le type du résultat .

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

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

- Sinon, si soit est double, l'autre doit être converti en double.

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

- 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 unsigned long.

- Sinon, si l'un des opérandes est un long int et l'autre unsigned int, 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 unsigned long int.

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

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

[Note : sinon, le seul cas restant est que que les deux opérandes sont int ]

3 votes

... tant que l'autre type n'est ni l'un ni l'autre. double ni long double .

1 votes

Charles : C'est exact. J'ai cité la section pertinente de la norme pour clarifier davantage.

0 votes

Ainsi, un entier peut-il 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) ?

18voto

legends2k Points 6380

Puisque les autres réponses ne parlent pas des règles de C++11, en voici une. Extrait du standard C++11 (draft n3337) §5/9 (souligne la différence) :

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

- Si l'un des opérandes est de type énumération scopé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 long double, l'autre est converti en long double.

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

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

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

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

- Sinon, si les deux opérandes ont des types d'entiers signés ou si les deux ont des types d'entiers non signés, la fonction l'opérande dont le type a le rang de conversion d'entier le moins élevé est converti au type de l'opérande dont le rang est le plus élevé. opérande de rang supérieur.

- Sinon, si l'opérande qui a le type d'entier non signé a un rang supérieur ou égal à celui de l'opérande. rang du type de l'autre opérande, l'opérande de type entier signé est converti en le 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 au type de l'opérande de type entier signé.

- Dans le cas contraire, les deux opérandes doivent être convertis au type d'entier non signé correspondant au type de l'opérande de type entier signé.

Ver aquí pour une liste fréquemment mise à jour.

2 votes

Ces règles étaient les mêmes dans toutes les versions de C++, à l'exception des énumérations scopées qui ont été ajoutées dans C++11, bien sûr.

7voto

David Stone Points 3822

Cette réponse s'adresse en grande partie à un commentaire fait par @RafaDowgird :

"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 opérations ?) Est-ce vraiment dans la spécification C++ ?

N'oubliez pas que la norme C++ contient la règle très importante du "as-if". Voir la section 1.8 : Exécution du programme :

3) Cette disposition est parfois appelée la règle "as-if", parce qu'une implémentation est libre de ne pas tenir compte d'une exigence de la norme du moment que le résultat est le même que si l'exigence avait été respectée, dans la mesure où que l'on peut déterminer à partir du comportement observable du programme.

Le compilateur ne peut pas définir un int doit avoir une taille de 8 bits, même si elle est la plus rapide, puisque la norme impose une taille minimale de 16 bits. int .

Par conséquent, dans le cas d'un ordinateur théorique avec des opérations super rapides sur 8 bits, la promotion implicite à int pour l'arithmétique pourrait compter. Cependant, pour de nombreuses opérations, vous ne pouvez pas savoir si le compilateur a réellement effectué les opérations dans la précision d'un int et ensuite converti en un char à stocker dans votre variable, ou si les opérations ont été faites en char depuis le début.

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 passez à int vous obtiendrez 600, qui sera ensuite implicitement converti en une valeur de unsigned char ce qui revient à moduler 256, donnant ainsi un résultat final de 88. Si vous ne faisiez pas de telles promotions, vous devriez effectuer un enveloppement entre les deux premières additions, ce qui réduirait le problème de 200 + 200 + 200 a 144 + 200 soit 344, qui se réduit à 88. En d'autres termes, le programme ne connaît pas la différence, et le compilateur est donc libre d'ignorer le mandat d'effectuer des opérations intermédiaires en int si les opérandes ont un rang inférieur à int .

C'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 modulus.

4voto

James Kanze Points 96599

Si l'on exclut les types non signés, il existe un type ordonné hiérarchie ordonnée : signed char, short, int, long, long long, float, double, long double. Tout d'abord, tout ce qui précède int dans la hiérarchie ci-dessus sera converti en int. Ensuite, dans une opération binaire, le type de rang inférieur sera converti en type de rang supérieur, et le résultat sera le type du type supérieur. (Vous noterez que, d'après hiérarchie, chaque fois qu'un type à virgule flottante et un type intégral sont sont impliqués, le type intégral sera converti en type à virgule flottante. flottante).

L'absence de signature complique un peu les choses : elle perturbe le classement, et certaines parties du classement deviennent définies par l'implémentation. Pour cette raison il est préférable de ne pas mélanger signé et non signé dans la même même expression. (La plupart des experts du C++ semblent éviter les non signés à moins que sauf si des opérations sur les bits sont impliquées. C'est en tout cas ce que recommande Stroustrup recommande).

3 votes

Stroustrup peut recommander ce qu'il veut, mais l'utilisation d'un système de signature int pour 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 j'utilise unsigned par défaut et signed seulement quand j'ai une raison.

1 votes

C'est bien beau, underscore_d, jusqu'au jour où tu devras faire des soustractions. Le principal problème des 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 ensuite vous seriez ennuyé de découvrir qu'il se plante pour les vecteurs vides parce que size() - 1 renvoie 18446744073709551615.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X