web-dev-qa-db-fra.com

Arrondir le nombre à 2 décimales en C

Comment puis-je arrondir un flottant (tel que 37,777779) à deux décimales (37,78) en C?

193
Tyler

Si vous souhaitez simplement arrondir le nombre à des fins de sortie, la chaîne de formatage "%.2f" est bien la bonne réponse. Toutefois, si vous souhaitez réellement arrondir la valeur en virgule flottante pour un calcul ultérieur, utilisez l'une des méthodes suivantes:

#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 que vous pouvez choisir trois règles d’arrondi différentes: arrondi inférieur (c’est-à-dire tronquer après deux décimales), arrondi au plus proche et arrondi supérieur. Habituellement, vous voulez arrondir au plus proche.

Comme plusieurs autres l'ont souligné, en raison des particularités 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 proches.

Pour beaucoup (beaucoup!) Plus d'informations sur l'arrondissement, et en particulier sur les règles de rupture d'égalité pour arrondir au plus proche, voir l'article de Wikipedia sur Rounding .

381
Dale Hagglund

Utilisation de %. 2f dans printf. Il n’imprime que 2 points décimaux.

Exemple:

printf("%.2f", 37.777779);

Sortie:

37.77
70
Andrew Coleson

En supposant que vous parlez de la valeur pour l'impression, alors la réponse de Andrew Coleson et AraK est correcte:

printf("%.2f", 37.777779);

Mais notez que si vous souhaitez arrondir le nombre à exactement 37,78 pour une utilisation interne (par exemple, pour comparer avec une autre valeur), ce n'est pas une bonne idée, en raison du fonctionnement des nombres en virgule flottante: vous ne le faites généralement pas. vouloir faire des comparaisons d'égalité pour les virgules flottantes, 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-le.

Voir le lien dans réponse de Greg Hewgill à une question connexe , qui explique également pourquoi vous ne devez pas utiliser de virgule flottante pour les calculs financiers.

40
John Carter

Que dis-tu de ça:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
22
Daniil
printf("%.2f", 37.777779);

Si vous voulez écrire sur C-string:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
19
AraK

Il n’est pas possible d’arrondir un float à un autre float, car le arrondi float peut ne pas être représentable (limitation des nombres à virgule flottante). Par exemple, supposons 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 formatage.

11
Andrew Keeton

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 éditer n'importe quel double myDouble avec n après le point décimal avec un code tel que celui-ci:

std::cout << prd(myDouble,n);
7
synaptik

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;
6
ZeroCool

En C++ (ou en C avec des distributions de style C), vous pouvez créer la fonction suivante:

/* 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;
}

Alors, 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 ici pour que vous puissiez voir la logique. Il existe probablement des solutions plus simples, mais cela fonctionne bien pour moi, surtout que cela me permet d’ajuster le nombre de chiffres après la décimale selon mes besoins.

5
synaptik

Utilisez toujours la famille de fonctions printf pour cela. Même si vous voulez obtenir la valeur sous forme de float, il vaut mieux utiliser snprintf pour obtenir la valeur arrondie sous forme de chaîne, puis la ré-analyser 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, "%.2f", dp, val);
    double result = atof(buffer);
    free(buffer);
    return result;
}

Je dis cela parce que l'approche indiquée par la réponse actuellement la plus votée et plusieurs autres ici - en multipliant par 100, en arrondissant au nombre entier le plus proche et en divisant par 100 encore - est imparfaite de deux manières:

  • Pour certaines valeurs, les arrondis vont dans le mauvais sens car la multiplication par 100 modifie le chiffre décimal déterminant le sens d'arrondi de 4 à 5 ou inversement, en raison de l'imprécision des nombres en virgule flottante.
  • Pour certaines valeurs, multiplier puis diviser par 100 ne va pas de soi, ce qui signifie que même si aucun arrondi n'a lieu, le résultat final sera faux

Pour illustrer le premier type d'erreur - la direction d'arrondi étant parfois fausse - essayez de lancer ce programme:

int main(void) {
    // This number is EXACTLY representable as a double
    double x = 0.01499999999999999944488848768742172978818416595458984375;

    printf("x: %.50f", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.50f", res1);
    printf("Rounded with round, then divided: %.50f", 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. La réponse mathématiquement correcte lorsqu’elle est arrondie à deux décimales est 0,01. Bien entendu, 0.01 n'est pas exactement représentable en tant que double, mais nous nous attendons à ce que notre résultat soit le double le plus proche de 0,01. Utiliser snprintf nous donne ce résultat, mais utiliser round(100 * x) / 100 nous donne 0,02, ce qui est faux. Pourquoi? Parce que 100 * x nous donne exactement 1,5 comme résultat. Multiplier par 100 change donc la bonne direction pour arrondir.

Pour illustrer le type d'erreur en second lieu - le résultat est parfois erroné en raison de * 100 et / 100 n'étant pas vraiment inverses. autre - 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 maintenant n'a même pas une partie fractionnaire; c'est une valeur entière, juste enregistrée avec le type double. Donc, le résultat après arrondi devrait être le même nombre que celui avec lequel nous avions 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

Oops. Notre méthode snprintf renvoie à nouveau le bon résultat, mais l’approche multiplier-alors-rond-ensuite-diviser échoue. En effet, la valeur mathématiquement correcte de 8631192423766613.0 * 100, 863119242376661300.0, n'est pas exactement représentable sous forme de double; la valeur la plus proche est 863119242376661248.0. Lorsque vous divisez ce montant par 100, vous obtenez 8631192423766612.0, un numéro différent de celui avec lequel vous avez commencé.

J'espère que c'est une démonstration suffisante que l'utilisation de roundf pour arrondir à un nombre de décimales est interrompue et que vous devriez utiliser snprintf à la place. Si cela vous semble horrible, vous serez peut-être rassuré de savoir que c'est essentiellement ce que fait CPython .

4
Mark Amery

Utilisez float roundf(float x).

"Les fonctions d'arrondi arrondissent leur argument à la valeur entière la plus proche sous forme de virgule flottante, en arrondissant les demi-casse à zéro, quel que soit le sens d'arrondi actuel." C11dr §7.12.9.5

#include <math.h>
float y = roundf(x * 100.0f) / 100.0f; 

En fonction de votre implémentation float, les nombres pouvant sembler être à mi-chemin ne le sont pas. comme virgule flottante est typiquement orienté base-2. En outre, il est très difficile d’arriver au 0.01 le plus proche pour tous les cas "à mi-parcours".

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, lorsqu’il est converti en float, la valeur est 1.115000009537... et n’est plus "à mi-chemin", mais plus proche de 1.12 et arrondit à la le plus proche float de 1.120000004768...

"1.125" est "à mi-chemin" entre 1.12 et 1.13, une fois converti en float, sa valeur est exactement 1.125 et est "à mi-chemin". Il arrondit à 1.13 en raison de liens avec la règle même et au plus proche float de 1.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 arrondit à la le plus proche float de 1.129999995232...

Si code 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 incorrectement arrondit à float de 1.139999985695... en raison de la précision plus limitée de float par rapport à double. Cette valeur incorrecte peut être considérée comme correcte, en fonction des objectifs de codage.

4
chux
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));

}

Ici n est 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
3
user2331026

Définition du code:

#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))

Résultats :

a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430
2
Zyp Privacy

J'ai créé cette macro pour arrondir les nombres flottants. Ajoutez-le dans votre en-tête/en cours 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 :)

2
mou

Permettez-moi d’abord de tenter de justifier ma raison d’ajouter une autre réponse à cette question. Dans un monde idéal, arrondir n'est pas vraiment une grosse affaire. Toutefois, dans les systèmes réels, vous devrez peut-être faire face à plusieurs problèmes pouvant entraîner des arrondis qui pourraient ne pas correspondre à vos attentes. Par exemple, vous pouvez effectuer des calculs financiers lorsque les résultats finaux sont arrondis et affichés pour les utilisateurs sous forme de deux décimales; ces mêmes valeurs sont stockées avec une précision fixe dans une base de données pouvant inclure plus de 2 décimales (pour diverses raisons; il n’existe pas de nombre optimal d’emplacement à conserver ... dépend des situations spécifiques que chaque système doit supporter, par exemple de minuscules articles dont les prix sont des fractions de centimes par unité); et des calculs en virgule flottante effectués sur des valeurs où les résultats sont plus/moins epsilon. J'ai affronté ces problèmes et développé ma propre stratégie au fil des ans. Je ne prétends pas avoir fait face à tous les scénarios ou avoir la meilleure réponse, mais voici un exemple de mon approche jusqu'ici qui résout 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;
    }
}

Arrondir à 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, le résultat est 6.82. Ici, l’hypothèse est que le calcul a donné exactement 6.824999 et que la 7ème décimale est zéro.

Pour val = 6.8249999, le résultat est 6.83. La 7ème décimale étant 9, dans ce cas, la fonction Round(val,6) donne le résultat attendu. Dans ce cas, il pourrait y avoir un nombre quelconque de 9s de fin.

Pour val = 6.824999499999, le résultat est 6.83. Arrondir à la 8ème décimale dans un premier temps, c’est-à-dire Round(val,8), prend en charge le seul cas désagréable dans lequel un résultat calculé en virgule flottante se calcule en 6.8249995, mais est représenté en interne par 6.824999499999....

Enfin, l'exemple tiré de la question ...val = 37.777779 a pour résultat 37.78.

Cette approche pourrait être encore généralisée comme suit:

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 les flottants/doubles. Cela fonctionne aussi sur les valeurs négatives. Je ne sais pas si cette approche est mathématiquement correcte pour toutes les possibilités.

0
Tim D

... 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;

Bien sûr, si vous souhaitez supprimer les informations supplémentaires du numéro.

0
NikosD