Comment puis-je arrondir une valeur flottante (telle que 37,777779) à deux décimales (37,78) en C?
Comment puis-je arrondir une valeur flottante (telle que 37,777779) à deux décimales (37,78) en C?
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:
#include <math.h>
float val = 37.777779;
float rounded_down = floorf(val * 100) / 100; /* Result: 37.77 */
float nearest = roundf(val * 100) / 100; /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100; /* Result: 37.78 */
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 .
doubles
aussi? Ne semble pas faire le travail que je veux :( (en utilisant floor
et ceil
).
Utilisation de % .2f dans printf. Il n'imprime que 2 décimales.
Exemple:
printf("%.2f", 37.777779);
Production:
37.77
float
portée car cela val * 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:
printf("%.2f", 37.777779);
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.
printf("%.*f", (int)precision, (double)number);
Que dis-tu de ça:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Si vous souhaitez écrire sur la chaîne C:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Il n'y a pas moyen d'arrondir les uns float
aux autres float
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.
float
à n décimales, puis vous attendre à ce que le résultat ait toujours n décimales. Vous en aurez toujours un float
, mais pas celui que vous attendiez.
De plus, si vous utilisez C ++, vous pouvez simplement créer une fonction comme celle-ci:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Vous pouvez ensuite sortir n'importe quel double myDouble
avec des n
places après la virgule décimale avec un code comme celui-ci:
std::cout << prd(myDouble,n);
Vous pouvez toujours utiliser:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
exemple:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
En C ++ (ou en C avec des transtypages de style C), vous pouvez créer la fonction:
/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
int y=x;
double z=x-y;
double m=pow(10,numDecimals);
double q=z*m;
double r=round(q);
return static_cast<double>(y)+(1.0/m)*r;
}
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.
Utilisez toujours la printf
famille de fonctions pour cela. Même si vous souhaitez obtenir la valeur sous forme de flottant, il vaut mieux utiliser snprintf
pour obtenir la valeur arrondie sous forme de chaîne, puis l'analyser à nouveau avec atof
:
#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
double dround(double val, int dp) {
int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
char *buffer = malloc(charsNeeded);
snprintf(buffer, charsNeeded, "%.*f", dp, val);
double result = atof(buffer);
free(buffer);
return result;
}
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:
int main(void) {
// This number is EXACTLY representable as a double
double x = 0.01499999999999999944488848768742172978818416595458984375;
printf("x: %.50f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.50f\n", res1);
printf("Rounded with round, then divided: %.50f\n", res2);
}
Vous verrez cette sortie:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
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'utilisation round(100 * x) / 100
nous donne 0,02, ce qui est faux. Pourquoi? Parce que cela 100 * 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:
int main(void) {
double x = 8631192423766613.0;
printf("x: %.1f\n", x);
double res1 = dround(x, 2);
double res2 = round(100 * x) / 100;
printf("Rounded with snprintf: %.1f\n", res1);
printf("Rounded with round, then divided: %.1f\n", res2);
}
Notre nombre n'a désormais même plus de partie fractionnaire; c'est une valeur entière, juste stockée avec le typedouble
. 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:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
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 correcte 8631192423766613.0 * 100
, 863119242376661300.0
n'est pas exactement représentable comme un double; la valeur la plus proche est 863119242376661248.0
. Lorsque vous divisez cela par 100, vous obtenez 8631192423766612.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 à la snprintf
place. Si cela vous semble un horrible hack, vous serez peut-être rassuré par la connaissance que c'est essentiellement ce que fait CPython .
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
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
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 proche 0.01
dans tous les cas "à mi-chemin" est le plus difficile.
void r100(const char *s) {
float x, y;
sscanf(s, "%f", &x);
y = round(x*100.0)/100.0;
printf("%6s %.12e %.12e\n", s, x, y);
}
int main(void) {
r100("1.115");
r100("1.125");
r100("1.135");
return 0;
}
1.115 1.115000009537e+00 1.120000004768e+00
1.125 1.125000000000e+00 1.129999995232e+00
1.135 1.134999990463e+00 1.139999985695e+00
Bien que "1.115" soit "à mi-chemin" entre 1.11 et 1.12, une fois converti en float
, la valeur est 1.115000009537...
et n'est plus "à mi-chemin", mais plus proche de 1.12 et arrondie au plus proche float
de1.120000004768...
"1.125" est "à mi-chemin" entre 1.12 et 1.13, une fois converti en float
, la valeur est exactement 1.125
et est "à mi-chemin". Il arrondit vers 1,13 en raison de l'égalité des règles et arrondit au plus proche float
de1.129999995232...
Bien que "1.135" soit "à mi-chemin" entre 1.13 et 1.14, une fois converti en float
, la valeur est 1.134999990463...
et n'est plus "à mi-chemin", mais plus proche de 1.13 et arrondie au plus proche float
de1.129999995232...
Si le code est utilisé
y = roundf(x*100.0f)/100.0f;
Bien que "1.135" soit "à mi-chemin" entre 1.13 et 1.14, une fois converti en float
, la valeur est 1.134999990463...
et n'est plus "à mi-chemin", mais plus proche de 1.13 mais arrondit incorrectement à float
of en 1.139999985695...
raison de la précision plus limitée de float
vs. double
. Cette valeur incorrecte peut être considérée comme correcte, selon les objectifs de codage.
J'ai fait cette macro pour arrondir les nombres flottants. Ajoutez-le dans votre en-tête / être de fichier
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Voici un exemple:
float x = ROUNDF(3.141592, 100)
x est égal à 3,14 :)
double f_round(double dval, int n)
{
char l_fmtp[32], l_buf[64];
char *p_str;
sprintf (l_fmtp, "%%.%df", n);
if (dval>=0)
sprintf (l_buf, l_fmtp, dval);
else
sprintf (l_buf, l_fmtp, dval);
return ((double)strtod(l_buf, &p_str));
}
Voici n
le nombre de décimales
exemple:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
c'est énorme 3) le bizarre if
/ else
bloc où vous faites exactement la même chose dans chaque branche , et 4) l'utilisation trop compliquée de sprintf
pour construire le spécificateur de format pour un deuxième sprintf
appel; il est plus simple d'utiliser .*
et de passer la double valeur et le nombre de décimales comme arguments au même sprintf
appel.
#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))
a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
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:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
L'arrondi à 2 décimales pour la présentation d'un résultat peut être effectué comme suit:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Pour val = 6.825
, le résultat est 6.83
comme prévu.
Pour val = 6.824999
résultat est 6.82
. Ici, l'hypothèse est que le calcul a donné exactement 6.824999
et la 7ème décimale est zéro.
Car le val = 6.8249999
résultat est 6.83
. La 7ème décimale étant9
dans ce cas, la Round(val,6)
fonction donne le résultat attendu. Dans ce cas, il pourrait y avoir un nombre illimité de 9
s.
Car le val = 6.824999499999
résultat est 6.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 vers 6.8249995
, mais est représenté en interne comme 6.824999499999...
.
Enfin, l'exemple de la question ...val = 37.777779
traduit par 37.78
.
Cette approche pourrait être encore plus généralisée:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
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.
Code C simple pour arrondir un nombre:
float n = 3.56;
printf("%.f", n);
Cela produira:
4
... ou vous pouvez le faire à l'ancienne sans aucune bibliothèque:
float a = 37.777779;
int b = a; // b = 37
float c = a - b; // c = 0.777779
c *= 100; // c = 77.777863
int d = c; // d = 77;
a = b + d / (float)100; // a = 37.770000;
Cela bien sûr si vous souhaitez supprimer les informations supplémentaires du numéro.
cette fonction prend le nombre et la précision et renvoie le nombre arrondi
float roundoff(float num,int precision)
{
int temp=(int )(num*pow(10,precision));
int num1=num*pow(10,precision+1);
temp*=10;
temp+=5;
if(num1>=temp)
num1+=10;
num1/=10;
num1*=10;
num=num1/pow(10,precision+1);
return num;
}
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.
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.