Come posso arrotondare un valore float (come 37.777779) a due cifre decimali (37.78) in C?
Come posso arrotondare un valore float (come 37.777779) a due cifre decimali (37.78) in C?
Risposte:
Se vuoi solo arrotondare il numero per scopi di output, la "%.2f"
stringa di formato è davvero la risposta corretta. Tuttavia, se si desidera effettivamente arrotondare il valore in virgola mobile per ulteriori calcoli, qualcosa di simile al seguente funziona:
#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 */
Nota che ci sono tre diverse regole di arrotondamento che potresti voler scegliere: arrotondare per difetto (cioè, troncare dopo due cifre decimali), arrotondato al più vicino e arrotondato per eccesso. Di solito, vuoi arrotondare al più vicino.
Come molti altri hanno sottolineato, a causa delle stranezze della rappresentazione in virgola mobile, questi valori arrotondati potrebbero non essere esattamente i valori decimali "ovvi", ma saranno molto vicini.
Per molte (molto!) Ulteriori informazioni sull'arrotondamento, e in particolare sulle regole di pareggio per l'arrotondamento al più vicino, vedi l'articolo di Wikipedia sull'arrotondamento .
doubles
troppo in qualche modo? Non sembra fare il lavoro che desidero :( (usando floor
e ceil
).
Utilizzando % .2f in printf. Stampa solo 2 punti decimali.
Esempio:
printf("%.2f", 37.777779);
Produzione:
37.77
float
portata in quanto val * 100
potrebbe traboccare.
Supponendo che tu stia parlando del valore per la stampa, quindi Andrew Coleson e la risposta di AraK sono corrette:
printf("%.2f", 37.777779);
Ma nota che se stai mirando a arrotondare il numero esattamente a 37,78 per uso interno (ad esempio per confrontare un altro valore), allora questa non è una buona idea, a causa del modo in cui funzionano i numeri in virgola mobile: di solito non lo fai vuoi fare confronti di uguaglianza per virgola mobile, invece usa un valore target +/- un valore sigma. O codificare il numero come una stringa con una precisione nota e confrontarlo.
Vedi il link nella risposta di Greg Hewgill a una domanda correlata , che spiega anche perché non dovresti usare il virgola mobile per i calcoli finanziari.
printf("%.*f", (int)precision, (double)number);
Cosa ne pensi di questo:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Se vuoi scrivere su C-string:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Non c'è modo di arrotondare a un float
altro float
perché arrotondatofloat
potrebbe non essere rappresentabile (una limitazione dei numeri in virgola mobile). Ad esempio, supponiamo di arrotondare da 37.777779 a 37.78, ma il numero rappresentabile più vicino è 37.781.
Tuttavia, è possibile "arrotondare" a float
utilizzando una funzione stringa di formato.
float
a n decimali e quindi aspettarsi che il risultato abbia sempre n decimali. Riceverai comunque un float
, non solo quello che ti aspettavi.
Inoltre, se stai usando C ++, puoi semplicemente creare una funzione come questa:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
È quindi possibile generare un doppio myDouble
con i n
punti dopo la virgola con un codice come questo:
std::cout << prd(myDouble,n);
Puoi ancora usare:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
esempio:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
In C ++ (o in C con cast in stile C), è possibile creare la funzione:
/* 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;
}
Quindi std::cout << showDecimals(37.777779,2);
produrrebbe: 37.78.
Ovviamente non hai davvero bisogno di creare tutte e 5 le variabili in quella funzione, ma le lascio lì in modo da poter vedere la logica. Probabilmente ci sono soluzioni più semplici, ma questo funziona bene per me, soprattutto perché mi permette di regolare il numero di cifre dopo il decimale di cui ho bisogno.
Utilizzare sempre la printf
famiglia di funzioni per questo. Anche se si desidera ottenere il valore come float, è consigliabile utilizzare snprintf
per ottenere il valore arrotondato come stringa e quindi analizzarlo nuovamente con 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;
}
Lo dico perché l'approccio mostrato dalla risposta attualmente più votata e molti altri qui - moltiplicando per 100, arrotondando al numero intero più vicino e quindi dividendolo di nuovo per 100 - è imperfetto in due modi:
Per illustrare il primo tipo di errore - talvolta la direzione di arrotondamento è errata - prova a eseguire questo programma:
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);
}
Vedrai questo output:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Si noti che il valore con cui abbiamo iniziato era inferiore a 0,015, quindi la risposta matematicamente corretta quando si arrotonda a 2 decimali è 0,01. Ovviamente, 0,01 non è esattamente rappresentabile come doppio, ma prevediamo che il nostro risultato sia il doppio più vicino a 0,01. L'uso snprintf
ci dà quel risultato, ma l'uso round(100 * x) / 100
ci dà 0,02, che è sbagliato. Perché? Perché 100 * x
ci dà esattamente 1,5 come risultato. Moltiplicando per 100 cambia quindi la direzione corretta in cui arrotondare.
Per illustrare il secondo tipo di errore - il risultato a volte è sbagliato a causa * 100
e / 100
non è realmente inverso l'uno dall'altro - possiamo fare un esercizio simile con un numero molto grande:
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);
}
Il nostro numero ora non ha nemmeno una parte frazionaria; è un valore intero, appena memorizzato con type double
. Quindi il risultato dopo l'arrotondamento dovrebbe essere lo stesso numero con cui abbiamo iniziato, giusto?
Se esegui il programma sopra, vedrai:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Ops. Il nostro snprintf
metodo restituisce di nuovo il risultato giusto, ma l'approccio moltiplica, quindi arrotonda, quindi divide. Questo perché il valore matematicamente corretto di 8631192423766613.0 * 100
, 863119242376661300.0
non è esattamente rappresentabile come doppio; il valore più vicino è 863119242376661248.0
. Quando lo dividi per 100, ottieni8631192423766612.0
- un numero diverso da quello con cui hai iniziato.
Speriamo che sia una dimostrazione sufficiente che l'uso roundf
per arrotondare a un numero di cifre decimali è rotto e che snprintf
invece dovresti usare . Se ti sembra un orribile hack, forse sarai rassicurato dalla consapevolezza che è sostanzialmente ciò che fa CPython .
Uso float roundf(float x)
.
"Le funzioni di arrotondamento arrotondano il loro argomento al valore intero più vicino in formato a virgola mobile, arrotondando i casi a metà distanza dallo zero, indipendentemente dalla direzione di arrotondamento corrente." C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
A seconda float
dell'implementazione, i numeri che possono apparire a metà strada non lo sono. poiché il virgola mobile è in genere orientato alla base 2. Inoltre, arrotondare con precisione al più vicino 0.01
su tutti i casi "a metà strada" è molto impegnativo.
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
Sebbene "1.115" sia "a metà strada" tra 1,11 e 1,12, quando convertito in float
, il valore è 1.115000009537...
e non è più "a metà strada", ma più vicino a 1.12 e arrotondato al più vicino float
di1.120000004768...
"1.125" è "a metà strada" tra 1,12 e 1,13, quando convertito in float
, il valore è esattamente 1.125
ed è "a metà strada". Si arrotonda verso 1.13 a causa di legami regola anche e giri al più vicino float
di1.129999995232...
Sebbene "1.135" sia "a metà strada" tra 1,13 e 1,14, quando convertito in float
, il valore è 1.134999990463...
e non è più "a metà strada", ma più vicino a 1.13 e arrotondato al più vicino float
di1.129999995232...
Se viene utilizzato il codice
y = roundf(x*100.0f)/100.0f;
Sebbene "1.135" sia "a metà strada" tra 1,13 e 1,14, quando convertito in float
, il valore è 1.134999990463...
e non è più "a metà strada", ma più vicino a 1.13 ma arrotondato erroneamente a float
di a 1.139999985695...
causa della precisione più limitata di float
vs. double
. Questo valore errato può essere visto come corretto, a seconda degli obiettivi di codifica.
Ho creato questa macro per arrotondare i numeri float. Aggiungilo nella tua intestazione / essendo del file
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Ecco un esempio:
float x = ROUNDF(3.141592, 100)
x è uguale a 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));
}
Qui n
il numero di decimali
esempio:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
è enorme 3) lo strano if
/ else
blocco in cui si fa esattamente la stessa cosa in ogni ramo e 4) l'uso complicato di sprintf
costruire l'identificatore di formato per una seconda sprintf
chiamata; è più semplice utilizzare .*
e passare il doppio valore e il numero di cifre decimali come argomenti alla stessa sprintf
chiamata.
#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
Consentitemi innanzitutto di giustificare il motivo per cui ho aggiunto un'altra risposta a questa domanda. In un mondo ideale, arrotondare non è davvero un grosso problema. Tuttavia, nei sistemi reali, potrebbe essere necessario affrontare diversi problemi che possono comportare arrotondamenti che potrebbero non essere quelli previsti. Ad esempio, è possibile che tu stia eseguendo calcoli finanziari in cui i risultati finali sono arrotondati e visualizzati agli utenti con 2 cifre decimali; questi stessi valori sono archiviati con precisione fissa in un database che può includere più di 2 cifre decimali (per vari motivi; non esiste un numero ottimale di posizioni da conservare ... dipende dalle situazioni specifiche che ciascun sistema deve supportare, ad esempio articoli di piccole dimensioni i cui prezzi sono frazioni di un centesimo per unità); e, calcoli in virgola mobile eseguiti su valori in cui i risultati sono più / meno epsilon. Ho affrontato questi problemi e ho sviluppato la mia strategia nel corso degli anni. Non pretenderò di aver affrontato tutti gli scenari o di avere la risposta migliore, ma di seguito è riportato un esempio del mio approccio finora che supera questi problemi:
Supponiamo che 6 decimali siano considerati una precisione sufficiente per i calcoli su float / doppi (una decisione arbitraria per l'applicazione specifica), usando la seguente funzione / metodo di arrotondamento:
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'arrotondamento al secondo decimale per la presentazione di un risultato può essere eseguito come:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Per val = 6.825
, il risultato è 6.83
come previsto.
Per val = 6.824999
, il risultato è 6.82
. Qui il presupposto è che il calcolo è risultato esattamente 6.824999
e il settimo decimale è zero.
Per val = 6.8249999
, il risultato è 6.83
. Il settimo decimale 9
in questo caso fa sì che la Round(val,6)
funzione dia il risultato atteso. In questo caso, potrebbe esserci un numero qualsiasi di messaggi finali 9
.
Per val = 6.824999499999
, il risultato è 6.83
. L'arrotondamento all'ottava cifra decimale come primo passo, vale a dire Round(val,8)
, si occupa dell'unico caso in cui viene calcolato un risultato in virgola mobile calcolato 6.8249995
, ma viene rappresentato internamente come 6.824999499999...
.
Infine, l'esempio della domanda ... val = 37.777779
risulta 37.78
.
Questo approccio potrebbe essere ulteriormente generalizzato come:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
dove N è la precisione da mantenere per tutti i calcoli intermedi su float / doppi. Questo funziona anche su valori negativi. Non so se questo approccio sia matematicamente corretto per tutte le possibilità.
Codice C semplice per arrotondare un numero:
float n = 3.56;
printf("%.f", n);
Questo produrrà:
4
... oppure puoi farlo alla vecchia maniera senza librerie:
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;
Questo ovviamente se si desidera rimuovere le informazioni extra dal numero.
questa funzione prende il numero e la precisione e restituisce il numero arrotondato
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;
}
converte il numero in virgola mobile in int spostando a sinistra il punto e verificando la condizione maggiore di cinque.
float
(edouble
) non sono virgola mobile decimale - sono virgola mobile binaria - quindi arrotondare a posizioni decimali non ha senso. È possibile arrotondare l'output, tuttavia.