Pourquoi la division de deux int ne donne-t-elle pas la bonne valeur lorsqu'elle est affectée à doubler?

110

Comment cela se fait-il dans l'extrait suivant

int a = 7;
int b = 3;
double c = 0;
c = a / b;

cfinit par avoir la valeur 2, plutôt que 2,3333, comme on pouvait s'y attendre. Si aet bsont doubles, la réponse passe à 2,333. Mais sûrement parce que c déjà est un double, il aurait dû fonctionner avec des entiers?

Alors pourquoi int/int=doublene fonctionne pas?

Jahoe
la source
Le doublon possible du résultat
phuclv

Réponses:

161

En effet, vous utilisez la version de division entière de operator/, qui prend 2 ints et renvoie un int. Pour utiliser la doubleversion qui renvoie a double, au moins un des ints doit être explicitement converti en a double.

c = a/(double)b;
Tchad La Guardia
la source
9
Je préférerais convertir explicitement à la fois aet bà doubletout simplement pour plus de clarté, mais il n'a pas d' importance.
John Dibling le
31
Puisque la question est étiquetée C ++, je préférerais voir static_cast <> plutôt qu'un cast C.
Martin York du
16
Personnellement, je pense que les moulages de style C sont plus clairs (le casting dans la plupart des autres langages courants se fait à la manière du style C). static_cast<>m'a toujours paru long et essoufflé. Dans le cas des primitives, il n'y a pas vraiment de danger d'obtenir static_cast<>et reinterpret_cast<>en mixte.
Chad La Guardia du
6
@ Tux-D: Pour les moulages arithmétiques? Je préférerais éviter static_castdans ce cas et utiliser plutôt une fonte de style C. Il n'y a aucun avantage à utiliser des transtypages de style C ++ ici et ils encombrent le code beaucoup plus que des casts de style C. La distribution arithmétique est exactement le contexte où les moulages de style C sont parfaitement appropriés et en fait plus appropriés que les autres moulages.
AnT le
19
Parfois, vous pouvez déjouer les gens "sans style C" en écrivant double(b). Ils ne se rendent pas toujours compte qu'il s'agit d'une conversion, car cela ressemble à un appel de constructeur explicite.
Steve Jessop du
12

C'est ici:

a) La division de deux ints effectue toujours une division entière. Ainsi, le résultat de a/bdans votre cas ne peut être qu'un int.

Si vous voulez garder aet en btant que ints, tout en les divisant complètement, vous devez lancer au moins l'un d'entre eux pour doubler: (double)a/bou a/(double)bou (double)a/(double)b.

b) cest un double, donc il peut accepter une intvaleur lors de l'affectation: le intest automatiquement converti doubleet affecté à c.

c) Rappelez-vous que lors de l'assignation, l'expression à droite de =est calculée en premier (selon la règle (a) ci-dessus, et sans tenir compte de la variable à gauche de =), puis affectée à la variable à gauche de =(selon ( b) ci-dessus). Je crois que cela complète le tableau.

nplatis
la source
11

À quelques exceptions près (je ne peux penser qu'à une seule), C ++ détermine la signification entière d'une expression (ou sous-expression) à partir de l'expression elle-même. Ce que vous faites avec les résultats de l'expression n'a pas d'importance. Dans votre cas, dans l'expression a / b, il n'y a pas doublede vue; tout est int. Le compilateur utilise donc la division entière. Ce n'est qu'une fois qu'il a le résultat qu'il considère ce qu'il doit en faire et le convertit en double.

James Kanze
la source
3
La seule exception à laquelle je peux penser est le choix d'une surcharge de fonction lors de la prise d'un pointeur - la valeur de &funcnamedépend du type vers lequel vous la castez.
Steve Jessop le
2
@Steve Jessop C'est la seule exception à laquelle je puisse penser. (Mais étant donné la taille et la complexité de la norme, je ne voudrais pas jurer que je n'en ai manqué aucune.)
James Kanze
6

cest une doublevariable, mais la valeur qui lui est assignée est une intvaleur car elle résulte de la division de deux ints, ce qui vous donne une "division entière" (en supprimant le reste). Donc, ce qui se passe dans la ligne c=a/best

  1. a/b est évalué, créant un temporaire de type int
  2. la valeur du temporaire est affectée caprès la conversion en type double.

La valeur de a/best déterminée sans référence à son contexte (affectation à double).

Fred Foo
la source
6

Lorsque vous divisez deux entiers, le résultat sera un entier, indépendamment du fait que vous le stockiez dans un double.

Alok Save
la source
5

En langage C ++, le résultat de la sous-expression n'est jamais affecté par le contexte environnant (à quelques rares exceptions près). C'est l'un des principes que le langage suit attentivement. L'expression c = a / bcontient une sous a / b- expression indépendante , qui est interprétée indépendamment de tout ce qui se trouve en dehors de cette sous-expression. La langue ne se soucie pas que vous attribuiez ultérieurement le résultat à un fichier double. a / best une division entière. Tout le reste n'a pas d'importance. Vous verrez ce principe suivi dans de nombreux coins de la spécification du langage. Voilà comment fonctionne C ++ (et C).

Un exemple d'exception que j'ai mentionné ci-dessus est l'affectation / l'initialisation du pointeur de fonction dans les situations de surcharge de fonction

void foo(int);
void foo(double);

void (*p)(double) = &foo; // automatically selects `foo(fouble)`

Il s'agit d'un contexte où le côté gauche d'une affectation / initialisation affecte le comportement du côté droit. (De plus, l'initialisation de la référence au tableau empêche la décroissance du type de tableau, ce qui est un autre exemple de comportement similaire.) Dans tous les autres cas, le côté droit ignore complètement le côté gauche.

Fourmi
la source
4

L' /opérateur peut être utilisé pour la division entière ou la division en virgule flottante. Vous lui donnez deux opérandes entiers, donc il fait une division entière, puis le résultat est stocké dans un double.

Vicky
la source
2

C'est techniquement une langue dépendante, mais presque toutes les langues traitent ce sujet de la même manière. Lorsqu'il y a une incompatibilité de type entre deux types de données dans une expression, la plupart des langages essaieront de convertir les données d'un côté de =pour qu'elles correspondent aux données de l'autre côté selon un ensemble de règles prédéfinies.

Lorsque vous divisez deux nombres du même type (entiers, doubles, etc.), le résultat sera toujours du même type (donc «int / int» donnera toujours int).

Dans ce cas, vous avez double var = integer result qui convertit le résultat entier en un double après le calcul, auquel cas les données fractionnaires sont déjà perdues. (la plupart des langages effectueront cette conversion pour éviter les inexactitudes de type sans déclencher d'exception ou d'erreur).

Si vous souhaitez conserver le résultat comme un double, vous allez vouloir créer une situation où vous avez double var = double result

Le moyen le plus simple de le faire est de forcer l'expression du côté droit d'une équation à doubler:

c = a/(double)b

La division entre un entier et un double résultera en un transtypage de l'entier en double (notez qu'en faisant des maths, le compilateur "va souvent" vers le type de données le plus spécifique, ceci afin d'éviter la perte de données).

Après l'upcast, a terminera comme un double et maintenant vous avez la division entre deux doubles. Cela créera la division et l'affectation souhaitées.

ENCORE, veuillez noter que cela est spécifique au langage (et peut même être spécifique au compilateur), cependant presque tous les langages (certainement tous ceux auxquels je peux penser par le haut de ma tête) traitent cet exemple de la même manière.

matthewdunnam
la source
Cette question est étiquetée [C ++], et le standard C ++ dicte exactement comment cela fonctionne. Je ne sais pas ce que vous entendez par «spécifique au langage» et ce n'est certainement pas spécifique au compilateur, en supposant qu'aucune extension de compilateur n'est engagée.
John Dibling le
De plus, il est incorrect de dire que "double var = résultat entier qui transforme le double var en int". Le double n'est pas converti en un entier. Le résultat int est converti en un double.
John Dibling le
J'autorisais la possibilité d'extensions de compilateur (j'ai en fait eu ce problème une fois où mon environnement "diffusait mal" les résultats et je ne pouvais pas comprendre pourquoi). Et le résultat est spécifique à la langue, car certaines langues ne suivent pas les mêmes règles de diffusion. Je n'ai pas considéré que c'était une balise spécifique au C ++. Vous avez raison sur le commentaire "double var = integer result". Édité pour refléter cela. Je vous remercie!
matthewdunnam le
0

L'important est que l'un des éléments du calcul soit de type float-double. Ensuite, pour obtenir un double résultat, vous devez lancer cet élément comme indiqué ci-dessous:

c = static_cast<double>(a) / b;

ou c = a / static_cast (b);

Ou vous pouvez le créer directement:

c = 7.0 / 3;

Notez que l'un des éléments de calcul doit avoir le '.0' pour indiquer une division d'un type float-double par un entier. Sinon, bien que la variable c soit un double, le résultat sera également nul.

L'architecte
la source
Que signifie votre réponse qu'aucune des 9 autres réponses n'est déjà présente?
bolov