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 .
doublestroppo in qualche modo? Non sembra fare il lavoro che desidero :( (usando floore ceil).
Utilizzando % .2f in printf. Stampa solo 2 punti decimali.
Esempio:
printf("%.2f", 37.777779);
Produzione:
37.77
floatportata in quanto val * 100potrebbe 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 floataltro floatperché 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 floatutilizzando una funzione stringa di formato.
floata 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 myDoublecon i npunti 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 printffamiglia di funzioni per questo. Anche se si desidera ottenere il valore come float, è consigliabile utilizzare snprintfper 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 snprintfci dà quel risultato, ma l'uso round(100 * x) / 100ci dà 0,02, che è sbagliato. Perché? Perché 100 * xci 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 * 100e / 100non è 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 snprintfmetodo 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.0non è 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 roundfper arrotondare a un numero di cifre decimali è rotto e che snprintfinvece 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 floatdell'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.01su 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 floatdi1.120000004768...
"1.125" è "a metà strada" tra 1,12 e 1,13, quando convertito in float, il valore è esattamente 1.125ed è "a metà strada". Si arrotonda verso 1.13 a causa di legami regola anche e giri al più vicino floatdi1.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 floatdi1.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 floatdi a 1.139999985695...causa della precisione più limitata di floatvs. 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/ elseblocco in cui si fa esattamente la stessa cosa in ogni ramo e 4) l'uso complicato di sprintfcostruire l'identificatore di formato per una seconda sprintfchiamata; è più semplice utilizzare .*e passare il doppio valore e il numero di cifre decimali come argomenti alla stessa sprintfchiamata.
#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.83come previsto.
Per val = 6.824999, il risultato è 6.82. Qui il presupposto è che il calcolo è risultato esattamente 6.824999e il settimo decimale è zero.
Per val = 6.8249999, il risultato è 6.83. Il settimo decimale 9in 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.777779risulta 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.