Comment puis-je arrondir une valeur flottante (telle que 37,777779) à deux décimales (37,78) en C?
c
floating-point
decimal
Sakib Arifin
la source
la source
float
(etdouble
) ne sont pas des virgules flottantes décimales - ce sont des virgules flottantes binaires - donc l'arrondi aux positions décimales n'a pas de sens. Vous pouvez cependant arrondir la sortie.Réponses:
Si vous souhaitez simplement arrondir le nombre à des fins de sortie, la
"%.2f"
chaîne de format est en effet la bonne réponse. Cependant, si vous voulez réellement arrondir la valeur à virgule flottante pour un calcul ultérieur, quelque chose comme le suivant fonctionne:Notez qu'il existe trois règles d'arrondi différentes que vous pouvez choisir: arrondir vers le bas (c'est-à-dire tronquer après deux décimales), arrondi au plus proche et arrondir vers le haut. Habituellement, vous voulez arrondir au plus proche.
Comme plusieurs autres l'ont souligné, en raison des caprices de la représentation en virgule flottante, ces valeurs arrondies peuvent ne pas être exactement les valeurs décimales "évidentes", mais elles seront très très proches.
Pour beaucoup (beaucoup!) Plus d'informations sur l'arrondi, et en particulier sur les règles de bris d'égalité pour l'arrondi au plus proche, voir l'article Wikipedia sur l'arrondi .
la source
doubles
aussi? Ne semble pas faire le travail que je veux :( (en utilisantfloor
etceil
).Utilisation de % .2f dans printf. Il n'imprime que 2 décimales.
Exemple:
Production:
la source
float
portée car celaval * 100
pourrait déborder.En supposant que vous parlez de la valeur d'impression, la réponse d' Andrew Coleson et d' AraK est correcte:
Mais notez que si vous visez à arrondir le nombre à exactement 37,78 pour un usage interne (par exemple pour comparer avec une autre valeur), ce n'est pas une bonne idée, en raison de la façon dont les nombres à virgule flottante fonctionnent: vous ne le faites généralement pas voulez faire des comparaisons d'égalité pour la virgule flottante, utilisez plutôt une valeur cible +/- une valeur sigma. Ou encodez le nombre sous forme de chaîne avec une précision connue, et comparez cela.
Voir le lien dans la réponse de Greg Hewgill à une question connexe , qui explique également pourquoi vous ne devriez pas utiliser la virgule flottante pour les calculs financiers.
la source
printf("%.*f", (int)precision, (double)number);
Que dis-tu de ça:
la source
Si vous souhaitez écrire sur la chaîne C:
la source
Il n'y a pas moyen d'arrondir les uns
float
aux autresfloat
car les arrondisfloat
peut ne pas être représentable (limitation des nombres à virgule flottante). Par exemple, disons que vous arrondissez 37,777779 à 37,78, mais le nombre représentable le plus proche est 37,781.Cependant, vous pouvez "arrondir" un
float
en utilisant une fonction de chaîne de format.la source
float
à n décimales, puis vous attendre à ce que le résultat ait toujours n décimales. Vous en aurez toujours unfloat
, mais pas celui que vous attendiez.De plus, si vous utilisez C ++, vous pouvez simplement créer une fonction comme celle-ci:
Vous pouvez ensuite sortir n'importe quel double
myDouble
avec desn
places après la virgule décimale avec un code comme celui-ci:la source
Vous pouvez toujours utiliser:
exemple:
la source
En C ++ (ou en C avec des transtypages de style C), vous pouvez créer la fonction:
Ensuite
std::cout << showDecimals(37.777779,2);
produirait: 37,78.Évidemment, vous n'avez pas vraiment besoin de créer les 5 variables dans cette fonction, mais je les laisse là pour que vous puissiez voir la logique. Il existe probablement des solutions plus simples, mais cela fonctionne bien pour moi - d'autant plus que cela me permet d'ajuster le nombre de chiffres après la décimale selon mes besoins.
la source
Utilisez toujours la
printf
famille de fonctions pour cela. Même si vous souhaitez obtenir la valeur sous forme de flottant, il vaut mieux utilisersnprintf
pour obtenir la valeur arrondie sous forme de chaîne, puis l'analyser à nouveau avecatof
:Je dis cela parce que l'approche montrée par la réponse actuellement la plus votée et plusieurs autres ici - multiplier par 100, arrondir à l'entier le plus proche, puis diviser par 100 à nouveau - est défectueuse de deux manières:
Pour illustrer le premier type d'erreur - la direction d'arrondi étant parfois erronée - essayez d'exécuter ce programme:
Vous verrez cette sortie:
Notez que la valeur avec laquelle nous avons commencé était inférieure à 0,015, et donc la réponse mathématiquement correcte lors de l'arrondi à 2 décimales est 0,01. Bien sûr, 0,01 n'est pas exactement représentable comme un double, mais nous nous attendons à ce que notre résultat soit le double le plus proche de 0,01. L'utilisation
snprintf
nous donne ce résultat, mais l'utilisationround(100 * x) / 100
nous donne 0,02, ce qui est faux. Pourquoi? Parce que cela100 * x
nous donne exactement 1,5 comme résultat. La multiplication par 100 change donc la direction correcte pour arrondir.Pour illustrer le deuxième type d'erreur - le résultat étant parfois erroné à cause de
* 100
et/ 100
pas vraiment inverses les uns des autres - nous pouvons faire un exercice similaire avec un très grand nombre:Notre nombre n'a désormais même plus de partie fractionnaire; c'est une valeur entière, juste stockée avec le type
double
. Donc, le résultat après arrondi devrait être le même nombre que celui avec lequel nous avons commencé, non?Si vous exécutez le programme ci-dessus, vous verrez:
Oups. Notre
snprintf
méthode renvoie à nouveau le bon résultat, mais l'approche multiplier puis arrondir puis diviser échoue. En effet , la valeur mathématiquement correcte8631192423766613.0 * 100
,863119242376661300.0
n'est pas exactement représentable comme un double; la valeur la plus proche est863119242376661248.0
. Lorsque vous divisez cela par 100, vous obtenez8631192423766612.0
- un nombre différent de celui avec lequel vous avez commencé.J'espère que c'est une démonstration suffisante que l'utilisation
roundf
de l'arrondi à un certain nombre de décimales est rompue et que vous devriez utiliser à lasnprintf
place. Si cela vous semble un horrible hack, vous serez peut-être rassuré par la connaissance que c'est essentiellement ce que fait CPython .la source
Utilisez
float roundf(float x)
."Les fonctions d'arrondi arrondissent leur argument à la valeur entière la plus proche au format à virgule flottante, arrondissant à mi-chemin les cas loin de zéro, quelle que soit la direction d'arrondi actuelle." C11dr §7.12.9.5
Selon votre
float
implémentation, les chiffres qui peuvent sembler être à mi-chemin ne le sont pas. comme virgule flottante est généralement orientée base-2. De plus, l'arrondi précis au plus proche0.01
dans tous les cas "à mi-chemin" est le plus difficile.Bien que "1.115" soit "à mi-chemin" entre 1.11 et 1.12, une fois converti en
float
, la valeur est1.115000009537...
et n'est plus "à mi-chemin", mais plus proche de 1.12 et arrondie au plus prochefloat
de1.120000004768...
"1.125" est "à mi-chemin" entre 1.12 et 1.13, une fois converti en
float
, la valeur est exactement1.125
et est "à mi-chemin". Il arrondit vers 1,13 en raison de l'égalité des règles et arrondit au plus prochefloat
de1.129999995232...
Bien que "1.135" soit "à mi-chemin" entre 1.13 et 1.14, une fois converti en
float
, la valeur est1.134999990463...
et n'est plus "à mi-chemin", mais plus proche de 1.13 et arrondie au plus prochefloat
de1.129999995232...
Si le code est utilisé
Bien que "1.135" soit "à mi-chemin" entre 1.13 et 1.14, une fois converti en
float
, la valeur est1.134999990463...
et n'est plus "à mi-chemin", mais plus proche de 1.13 mais arrondit incorrectement àfloat
of en1.139999985695...
raison de la précision plus limitée defloat
vs.double
. Cette valeur incorrecte peut être considérée comme correcte, selon les objectifs de codage.la source
J'ai fait cette macro pour arrondir les nombres flottants. Ajoutez-le dans votre en-tête / être de fichier
Voici un exemple:
x est égal à 3,14 :)
la source
Voici
n
le nombre de décimalesexemple:
la source
dval
c'est énorme 3) le bizarreif
/else
bloc où vous faites exactement la même chose dans chaque branche , et 4) l'utilisation trop compliquée desprintf
pour construire le spécificateur de format pour un deuxièmesprintf
appel; il est plus simple d'utiliser.*
et de passer la double valeur et le nombre de décimales comme arguments au mêmesprintf
appel.Définition du code:
Résultats :
la source
Permettez-moi d'abord d'essayer de justifier ma raison d'ajouter une autre réponse à cette question. Dans un monde idéal, l'arrondi n'est pas vraiment un gros problème. Cependant, dans les systèmes réels, vous devrez peut-être faire face à plusieurs problèmes qui peuvent entraîner des arrondis qui ne correspondent peut-être pas à vos attentes. Par exemple, vous pouvez effectuer des calculs financiers où les résultats finaux sont arrondis et affichés aux utilisateurs sous la forme de 2 décimales; ces mêmes valeurs sont stockées avec une précision fixe dans une base de données qui peut inclure plus de 2 décimales (pour diverses raisons; il n'y a pas de nombre optimal de lieux à conserver ... dépend des situations spécifiques que chaque système doit prendre en charge, par exemple de petits articles dont les prix sont des fractions d'un sou par unité); et des calculs en virgule flottante effectués sur des valeurs où les résultats sont plus / moins epsilon. J'ai été confronté à ces problèmes et j'ai développé ma propre stratégie au fil des ans. Je ne prétendrai pas avoir fait face à tous les scénarios ou avoir la meilleure réponse, mais voici un exemple de mon approche jusqu'à présent qui surmonte ces problèmes:
Supposons que 6 décimales soient considérées comme une précision suffisante pour les calculs sur les flottants / doubles (une décision arbitraire pour l'application spécifique), en utilisant la fonction / méthode d'arrondi suivante:
L'arrondi à 2 décimales pour la présentation d'un résultat peut être effectué comme suit:
Pour
val = 6.825
, le résultat est6.83
comme prévu.Pour
val = 6.824999
résultat est6.82
. Ici, l'hypothèse est que le calcul a donné exactement6.824999
et la 7ème décimale est zéro.Car le
val = 6.8249999
résultat est6.83
. La 7ème décimale étant9
dans ce cas, laRound(val,6)
fonction donne le résultat attendu. Dans ce cas, il pourrait y avoir un nombre illimité de9
s.Car le
val = 6.824999499999
résultat est6.83
. Arrondi à la 8ème décimale dans un premier temps, c.-à-d.Round(val,8)
, prend soin du seul cas désagréable par lequel un résultat en virgule flottante calculé calcule vers6.8249995
, mais est représenté en interne comme6.824999499999...
.Enfin, l'exemple de la question ...
val = 37.777779
traduit par37.78
.Cette approche pourrait être encore plus généralisée:
où N est la précision à maintenir pour tous les calculs intermédiaires sur flottants / doubles. Cela fonctionne également sur les valeurs négatives. Je ne sais pas si cette approche est mathématiquement correcte pour toutes les possibilités.
la source
Code C simple pour arrondir un nombre:
Cela produira:
la source
... ou vous pouvez le faire à l'ancienne sans aucune bibliothèque:
Cela bien sûr si vous souhaitez supprimer les informations supplémentaires du numéro.
la source
cette fonction prend le nombre et la précision et renvoie le nombre arrondi
il convertit le nombre à virgule flottante en int en décalant le point vers la gauche et en vérifiant la condition supérieure à cinq.
la source