Nous avons appris dans Introduction à la programmation que si vous divisez deux entiers, vous obtenez toujours un entier. Pour résoudre le problème, faites au moins un de ces nombres entiers un flottant.
Pourquoi le compilateur ne comprend-il pas que je veux que le résultat soit un nombre décimal?
Commentaires
Réponse
Pourquoi le compilateur ne comprend-il pas que je veux que le résultat soit un nombre décimal?
Le compilateur C ++ suit simplement les règles bien définies et déterministes comme indiqué dans la norme C ++. La norme C ++ a ces règles car le comité des normes a décidé de procéder de cette façon.
Ils auraient pu écrire la norme pour dire que les nombres entiers donnent des nombres à virgule flottante, ou ne le font que dans le cas dun reste. Cependant, cela ajoute de la complexité: je dois soit savoir à lavance quel est le résultat, soit peut-être reconvertir en entier si cela donne toujours un flottant. Peut-être que je veux un entier .
Lune des philosophies fondamentales du C ++ est « vous ne payez pas pour ce que vous nutilisez pas . » Si vous voulez la complexité du mélange des nombres entiers et des flottants (et que les instructions supplémentaires du processeur et laccès à la mémoire impliquent 1 ), alors effectuez le cast de type comme vous lavez mentionné dans votre question . Sinon, tenez-vous-en aux mathématiques entières standard.
Enfin, le mélange de variables intégrales et à virgule flottante peut entraîner une perte de précision et parfois des résultats incorrects comme je lexplique ci-dessous. Si vous le souhaitez, payez-le: sinon, la norme impose aux compilateurs de sen tenir à un ensemble strict de règles pour mélanger les types de données. Cest un comportement bien défini: en tant que développeur C ++, je peux rechercher cela dans la norme et voir comment cela fonctionne.
Il y a essentiellement trois façons de faire ce que vous essayez de faire, chacun avec des avantages et des inconvénients.
-
Mathématiques entières: cela aboutit à tronquer les résultats lors de la division comme vous lavez découvert. Si vous voulez la partie décimale, vous devez la traiter séparément en divisant, en obtenant le reste et en traitant la partie décimale comme le reste divisé par le diviseur. Cest une opération un peu plus complexe et a plus de variables à jongler.
-
Mathématiques à virgule flottante: cela produira généralement des résultats corrects (assez) pour de petites valeurs, mais peut introduire facilement des erreurs de précision et darrondi, dautant plus que lexposant augmente. Si vous divisez un grand nombre par un petit nombre, vous pouvez même provoquer un dépassement inférieur ou simplement obtenir un résultat erroné car les échelles des nombres ne jouent pas bien les unes avec les autres.
-
Faites vos propres calculs. Il existe des classes qui gèrent la précision étendue des nombres décimaux et rationnels . Celles-ci seront généralement plus lentes que les mathématiques sur les types intégrés, mais sont généralement encore assez rapides et fournissent des mathématiques de précision arbitraire. Les arrondis et autres problèmes ne sont pas automatiques comme ils le sont avec les flottants IEEE, mais vous gagnez plus de contrôle et certainement plus de précision.
La clé ici est de choisir en fonction du domaine du problème . Les trois méthodes de représentation des nombres ont leurs propres avantages et inconvénients. Vous utilisez un compteur de boucles? Choisissez un type intégral. Représenter des emplacements dans lespace 3D? Probablement un groupe de flotteurs. Vous voulez suivre votre argent? Utilisez un type décimal fixe.
1 Architectures de processeur les plus courantes (par exemple x86-64 ) aura des ensembles dinstructions séparés qui opèrent sur différents types de registres tels que les nombres entiers et flottants, ainsi que des instructions supplémentaires pour convertir entre intégrale, virgule flottante et diverses représentations de ceux-ci (signé et non signé, flottant et double). Certaines de ces opérations peuvent également impliquer un accès à la mémoire: convertir une valeur et la stocker en mémoire (sa variable). Les mathématiques au niveau du processeur ne sont pas aussi simples que « un entier entrant, sortant ». »Bien que lajout de deux entiers puisse être une opération très simple, peut-être une seule instruction, le mélange de types de données peut augmenter la complexité.
Commentaires
- Vous dites que la norme C ++ stipule que ce comportement devrait être le cas. Pourquoi? ‘ t cela rendrait-il les choses plus faciles à dire, » La division dentiers qui ne sont pas divisibles de manière égale donne des nombres flottants, toute autre division est un jeu équitable. »
- @ moonman239 voir mes modifications.
- @ moonman239 Pas pour les rédacteurs de compilateurs. De nombreuses architectures de processeur couramment utilisées fournissent un résultat entier lorsquon leur demande de faire une division avec deux entiers. Ils devraient implémenter une vérification des résultats non entiers, puis basculer pour utiliser la virgule flottante plus lente Sinon, ils auraient pu passer par défaut à la division en virgule flottante et perdre lintérêt de ceux qui voulaient des mathématiques rapides, de ceux qui voulaient des mathématiques précises et de ceux qui étaient habitués à C. Changer maintenant nest pas ‘ une option car cela briserait la compatibilité avec le code existant.
- Ce nest pas que vous préconiseriez cela comme alternative, mais en créant le type statique dune expression dépendent des valeurs d’exécution des opérandes qui ne fonctionnent pas ‘ avec le système de type statique de C ++ ‘.
- @ moonman239: Avoir une opération qui produit un type différent en fonction des valeurs des opérandes est une pure folie.
Réponse
Ceci est dû à lévolution du matériel. Au début des ordinateurs, toutes les machines navaient pas une unité à virgule flottante, le matériel nétait tout simplement pas capable de comprendre la notion de nombre à virgule flottante. Bien sûr, les nombres à virgule flottante peuvent être implémentés comme une abstraction logicielle, mais cela présente des inconvénients importants. Toute larithmétique sur ces machines devait être une arithmétique entière par défaut.
Et encore aujourdhui, il existe une distinction ferme entre les unités arithmétiques entières et flottantes dans un processeur. Leurs opérandes sont stockés dans des fichiers de registres séparés pour commencer, et une unité entière est câblée pour prendre deux arguments entiers et produire un résultat entier qui se termine dans un registre entier. Certains processeurs exigent même quune valeur entière soit stockée en mémoire, puis rechargée dans un registre à virgule flottante, avant de pouvoir être recodée en un nombre à virgule flottante, avant de pouvoir effectuer une division en virgule flottante dessus.
En tant que telle, la décision prise par les développeurs C au tout début du langage (C ++ a simplement hérité de ce comportement), était la seule décision appropriée à prendre, et reste valable aujourdhui: si vous avez besoin de calcul en virgule flottante, vous peut lutiliser. Si vous nen avez pas besoin, vous nêtes pas obligé de le faire.
Commentaires
- Cest triste que la plupart des contraintes qui existaient au la création du standard C ++ est assez obsolète aujourdhui! Par exemple: » vous ne payez pas ce que vous nutilisez pas. » de nos jours, le matériel est pris pour acquis et tout ce que les utilisateurs veulent cest exécution!
- @ mahen23 Tous les utilisateurs ne pensent pas comme ça. Je travaille dans un domaine où les programmes sont exécutés sur des milliers de cœurs CPU en parallèle. Dans ce domaine, lefficacité cest de largent, à la fois en termes dinvestissements et en termes de consommation électrique. Un langage comme Java ne supporte pas le fantôme dune chance dans ce domaine, contrairement au C ++.
- @ mahen23 Non, ce nest pas ‘ t – ou mieux, il seulement si vous regardez les architectures CPU actuelles pour les ordinateurs de bureau et supérieurs. Il existe encore de nombreux systèmes embarqués qui ne ‘ t ou ne prennent que partiellement en charge les opérations en virgule flottante, et C ainsi que C ++ continuent de les prendre en charge afin de fournir limplémentation la plus efficace possible à moins de en utilisant lassembleur. BTW, même des langages de niveau supérieur comme Python font la distinction entre les opérations entières et FP – essayez
10 / 3
.
Réponse
10/2 avec des nombres entiers vous donne exactement 5 – la bonne réponse.
Avec des calculs en virgule flottante, 10/2 pourrait donner la bonne réponse *.
En dautres termes, il est impossible que les nombres à virgule flottante soient « parfaits » sur le matériel actuel – seuls les nombres entiers peuvent être corrects, malheureusement, ils ne peuvent pas « t faire de décimales mais le travail est facile
Par exemple, au lieu de 4/3, faites (4 * 1000) / (3 * 1000) == 1333. Dessinez simplement un. dans le logiciel lors de laffichage de la réponse à votre utilisateur (1.333). Cela vous donne une réponse précise, au lieu dune réponse incorrecte par un certain nombre de décimales.
Les erreurs mathématiques en virgule flottante peuvent sadditionner et causer des erreurs importantes – tout ce qui est important (comme la finance) utilisera des nombres entiers .
* lexemple 10/2 sera en fait correct avec les calculs en virgule flottante, mais vous ne pouvez pas vous y fier, de nombreux autres nombres donnent des résultats incorrects …pour plus de détails, lisez: http://http.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps Le fait est que vous ne pouvez pas vous fier à la précision lorsque les virgules flottantes sont impliquées
Commentaires
- Les implémentations en virgule flottante conformes à la norme IEEE 754 vous donneront un résultat exact pour 10/2. En fait, elles vous donneront résultats pour toute opération impliquant uniquement des opérandes entiers qui ont un résultat entier à condition que les opérandes et le résultat puissent être représentés exactement, ce que peuvent être les entiers «assez petits».
- @ 5gon12eder là ‘ n n’a pas besoin de choisir un peu, je ‘ m essayant simplement de décrire un problème complexe en termes simples. L’intérêt de prendre en charge les valeurs non entières est d’avoir des décimales ( qui peut être fait en utilisant des entiers en multipliant simplement tout par le nombre de décimales que vous voulez comme je lai démontré).
Réponse
Bien que techniquement pas complètement correct, Le C ++ est toujours considéré comme un sur-ensemble de C, sen est inspiré et en tant que tel sest approprié certaines de ses propriétés, la division entière étant lune dentre elles.
C a été principalement conçu pour être efficace et rapide, et les entiers sont généralement beaucoup plus rapide que les virgules flottantes, car le type entier est lié au matériel, alors que les virgules flottantes doivent être calculées.
Lorsque lopérande /
reçoit deux entiers, un du côté gauche et celui du côté droit, il peut même ne pas faire de division du tout, le résultat peut être calculé en utilisant une simple addition et une boucle, en demandant combien de fois lopérande du côté droit sinsère dans lopérande de gauche.
//
est lopérateur de division dentier et#
est un commentaire sur une seule ligne. En Pascal, lopérateur de division dentier est le mot-clédiv
. Les deux fonctionnent assez bien pour leur langue respective ges. C fait probablement la pire chose possible: un opérateur qui peut faire deux choses complètement différentes basées sur un contexte arbitraire.