Wie kann ich einen Gleitkommawert (z. B. 37.777779) auf zwei Dezimalstellen (37.78) in C runden?
Wie kann ich einen Gleitkommawert (z. B. 37.777779) auf zwei Dezimalstellen (37.78) in C runden?
Antworten:
Wenn Sie die Zahl nur für Ausgabezwecke runden möchten, "%.2f"
ist die Formatzeichenfolge in der Tat die richtige Antwort. Wenn Sie jedoch den Gleitkommawert für die weitere Berechnung tatsächlich runden möchten, funktioniert Folgendes:
#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 */
Beachten Sie, dass Sie möglicherweise drei verschiedene Rundungsregeln auswählen möchten: Abrunden (dh nach zwei Dezimalstellen abschneiden), auf die nächste gerundet und aufrunden. Normalerweise möchten Sie auf den nächsten runden.
Wie mehrere andere bereits betont haben, sind diese gerundeten Werte aufgrund der Besonderheiten der Gleitkommadarstellung möglicherweise nicht genau die "offensichtlichen" Dezimalwerte, liegen jedoch sehr nahe beieinander.
Weitere (viel!) Weitere Informationen zum Runden und insbesondere zu den Regeln für das Aufbrechen auf den nächsten Punkt finden Sie im Wikipedia-Artikel zum Runden .
doubles
irgendwie auch zum Laufen bringen? Scheint nicht den Job zu machen, den ich will :( (mit floor
und ceil
).
Verwenden von % .2f in printf. Es werden nur 2 Dezimalstellen gedruckt.
Beispiel:
printf("%.2f", 37.777779);
Ausgabe:
37.77
float
Reichweitenverluste auftreten, val * 100
die überlaufen könnten.
Angenommen, Sie sprechen über den Wert für das Drucken, dann sind die Antworten von Andrew Coleson und AraK richtig:
printf("%.2f", 37.777779);
Beachten Sie jedoch, dass dies keine gute Idee ist, wenn Sie die Zahl für den internen Gebrauch auf genau 37,78 runden möchten (z. B. um sie mit einem anderen Wert zu vergleichen). Dies ist aufgrund der Funktionsweise von Gleitkommazahlen normalerweise keine gute Idee Wenn Sie Gleichheitsvergleiche für Gleitkommawerte durchführen möchten, verwenden Sie stattdessen einen Zielwert +/- einen Sigma-Wert. Oder codieren Sie die Zahl als Zeichenfolge mit bekannter Genauigkeit und vergleichen Sie diese.
Siehe den Link in Greg Hewgills Antwort auf eine verwandte Frage , in der auch erläutert wird , warum Sie Gleitkommawerte nicht für Finanzberechnungen verwenden sollten.
printf("%.*f", (int)precision, (double)number);
Wie wäre es damit:
float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);
printf("%.2f", 37.777779);
Wenn Sie in C-String schreiben möchten:
char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);
Es gibt keine Möglichkeit, a float
auf einen anderen zu runden , float
da die Rundung float
möglicherweise nicht darstellbar ist (eine Einschränkung der Gleitkommazahlen). Angenommen, Sie runden 37.777779 auf 37.78, aber die nächste darstellbare Zahl ist 37.781.
Sie können a jedoch float
mithilfe einer Formatzeichenfolgenfunktion "runden" .
float
auf n Dezimalstellen runden können und dann erwarten, dass das Ergebnis immer n Dezimalstellen hat. Sie werden immer noch eine bekommen float
, nur nicht die, die Sie erwartet haben.
Wenn Sie C ++ verwenden, können Sie auch einfach eine Funktion wie die folgende erstellen:
string prd(const double x, const int decDigits) {
stringstream ss;
ss << fixed;
ss.precision(decDigits); // set # places after decimal
ss << x;
return ss.str();
}
Sie können dann mit Code wie diesem ein beliebiges Double myDouble
mit n
Stellen nach dem Dezimalpunkt ausgeben :
std::cout << prd(myDouble,n);
Sie können weiterhin verwenden:
float ceilf(float x); // don't forget #include <math.h> and link with -lm.
Beispiel:
float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;
In C ++ (oder in C mit Casts im C-Stil) können Sie die folgende Funktion erstellen:
/* 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;
}
Dann std::cout << showDecimals(37.777779,2);
würde produzieren: 37,78.
Natürlich müssen Sie nicht wirklich alle 5 Variablen in dieser Funktion erstellen, aber ich lasse sie dort, damit Sie die Logik sehen können. Es gibt wahrscheinlich einfachere Lösungen, aber das funktioniert gut für mich - zumal ich die Anzahl der Stellen nach der Dezimalstelle nach Bedarf anpassen kann.
Verwenden Sie dazu immer die printf
Funktionsfamilie. Selbst wenn Sie den Wert als Float erhalten möchten, verwenden Sie am besten snprintf
den gerundeten Wert als Zeichenfolge und analysieren ihn anschließend mit 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;
}
Ich sage dies, weil der Ansatz, den die derzeit am besten gewählte Antwort und einige andere hier zeigen - Multiplizieren mit 100, Runden auf die nächste ganze Zahl und erneutes Teilen durch 100 - in zweierlei Hinsicht fehlerhaft ist:
Führen Sie dieses Programm aus, um die erste Art von Fehler zu veranschaulichen - die Rundungsrichtung ist manchmal falsch.
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);
}
Sie sehen diese Ausgabe:
x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406
Beachten Sie, dass der Wert, mit dem wir begonnen haben, weniger als 0,015 betrug. Daher lautet die mathematisch korrekte Antwort beim Runden auf 2 Dezimalstellen 0,01. Natürlich ist 0,01 nicht genau als Double darstellbar, aber wir erwarten, dass unser Ergebnis das Double ist, das 0,01 am nächsten kommt. Die Verwendung snprintf
gibt uns dieses Ergebnis, aber die Verwendung round(100 * x) / 100
gibt uns 0,02, was falsch ist. Warum? Denn 100 * x
gibt uns genau 1,5 als Ergebnis. Das Multiplizieren mit 100 ändert somit die richtige Richtung zum Runden.
Um die zweite Art von Fehler zu veranschaulichen - das Ergebnis ist manchmal falsch * 100
und / 100
nicht wirklich umgekehrt - können wir eine ähnliche Übung mit einer sehr großen Anzahl durchführen:
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);
}
Unsere Zahl hat jetzt nicht einmal einen Bruchteil; Es ist ein ganzzahliger Wert, der nur mit Typ gespeichert wird double
. Das Ergebnis nach dem Runden sollte also dieselbe Zahl sein, mit der wir begonnen haben, oder?
Wenn Sie das obige Programm ausführen, sehen Sie:
x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0
Hoppla. Unsere snprintf
Methode liefert wieder das richtige Ergebnis, aber der Ansatz "Multiplizieren, Runden, Teilen" schlägt fehl. Das ist , weil der mathematisch korrekte Wert 8631192423766613.0 * 100
, 863119242376661300.0
ist nicht exakt darstellbar als Doppel; der nächste Wert ist 863119242376661248.0
. Wenn Sie das durch 100 teilen, erhalten Sie 8631192423766612.0
- eine andere Zahl als die, mit der Sie begonnen haben.
Hoffentlich ist dies eine ausreichende Demonstration, dass die Verwendung roundf
zum Runden auf eine Anzahl von Dezimalstellen fehlerhaft ist und dass Sie snprintf
stattdessen verwenden sollten. Wenn sich das für Sie wie ein schrecklicher Hack anfühlt, werden Sie vielleicht durch das Wissen beruhigt sein, dass es im Grunde das ist, was CPython tut .
Verwenden Sie float roundf(float x)
.
"Die Rundungsfunktionen runden ihr Argument im Gleitkommaformat auf den nächsten ganzzahligen Wert und runden Fälle unabhängig von der aktuellen Rundungsrichtung auf halbem Weg von Null weg." C11dr §7.12.9.5
#include <math.h>
float y = roundf(x * 100.0f) / 100.0f;
Abhängig von Ihrer float
Implementierung sind Zahlen, die auf halbem Weg zu sein scheinen, nicht. als Gleitkomma ist typischerweise Basis-2-orientiert. Darüber hinaus 0.01
ist es am schwierigsten, in allen "halben" Fällen genau auf das nächste zu runden .
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
Obwohl "1.115" zwischen 1.11 und 1.12 "auf halbem Weg" liegt, float
ist 1.115000009537...
und ist der Wert bei der Umrechnung in " nicht mehr" auf halbem Weg ", sondern näher an 1.12 und rundet auf den nächsten Wert float
von1.120000004768...
"1.125" ist "auf halbem Weg" zwischen 1.12 und 1.13, wenn in konvertiert, float
ist der Wert genau 1.125
und ist "auf halbem Weg". Es rundet in Richtung 1,13, weil es gleichmäßige Regeln gibt, und rundet auf das nächste float
von1.129999995232...
Obwohl "1.135" zwischen 1.13 und 1.14 "auf halbem Weg" liegt, float
ist 1.134999990463...
und ist der Wert bei der Konvertierung in "nicht auf halbem Weg", sondern näher an 1.13 und rundet auf den nächsten Wert float
von1.129999995232...
Wenn Code verwendet wird
y = roundf(x*100.0f)/100.0f;
Obwohl „1,135“ ist „ auf halbem Wege“ zwischen 1,13 und 1,14, wenn umgewandelt float
, der Wert 1.134999990463...
und ist nicht mehr „ auf halbem Wege“, aber näher an 1,13 aber falsch Runden float
der 1.139999985695...
aufgrund der begrenzteren Genauigkeit float
vs. double
. Dieser falsche Wert kann abhängig von den Codierungszielen als korrekt angesehen werden.
Ich habe dieses Makro zum Runden von Float-Zahlen erstellt. Fügen Sie es in Ihren Header / Sein der Datei ein
#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))
Hier ist ein Beispiel:
float x = ROUNDF(3.141592, 100)
x entspricht 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));
}
Hier n
ist die Anzahl der Dezimalstellen
Beispiel:
double d = 100.23456;
printf("%f", f_round(d, 4));// result: 100.2346
printf("%f", f_round(d, 2));// result: 100.23
dval
es riesig ist 3) das Unheimliche if
/ else
Blockieren, bei dem Sie in jedem Zweig genau dasselbe tun und 4) die überkomplizierte Verwendung sprintf
zum Erstellen des Formatbezeichners für einen zweiten sprintf
Aufruf; Es ist einfacher, nur .*
den doppelten Wert und die Anzahl der Dezimalstellen als Argumente für denselben sprintf
Aufruf zu verwenden und zu übergeben.
#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
Lassen Sie mich zunächst versuchen, meinen Grund für die Hinzufügung einer weiteren Antwort auf diese Frage zu rechtfertigen. In einer idealen Welt ist das Runden keine große Sache. In realen Systemen müssen Sie sich jedoch möglicherweise mit mehreren Problemen auseinandersetzen, die zu Rundungen führen können, die möglicherweise nicht Ihren Erwartungen entsprechen. Beispielsweise führen Sie möglicherweise Finanzberechnungen durch, bei denen die Endergebnisse gerundet und den Benutzern als 2 Dezimalstellen angezeigt werden. Dieselben Werte werden mit fester Genauigkeit in einer Datenbank gespeichert, die mehr als 2 Dezimalstellen enthalten kann (aus verschiedenen Gründen; es gibt keine optimale Anzahl von Stellen, die beibehalten werden müssen ... hängt von bestimmten Situationen ab, die jedes System unterstützen muss, z. B. winzigen Elementen, deren Preise sind Bruchteile eines Pennys pro Einheit); und Gleitkommaberechnungen, die an Werten durchgeführt werden, bei denen die Ergebnisse plus / minus epsilon sind. Ich habe mich im Laufe der Jahre mit diesen Problemen auseinandergesetzt und meine eigene Strategie entwickelt. Ich werde nicht behaupten, dass ich mich jedem Szenario gestellt habe oder die beste Antwort habe, aber im Folgenden finden Sie ein Beispiel für meinen bisherigen Ansatz, der diese Probleme überwindet:
Angenommen, 6 Dezimalstellen werden als ausreichende Genauigkeit für Berechnungen von Floats / Doubles (eine willkürliche Entscheidung für die spezifische Anwendung) unter Verwendung der folgenden Rundungsfunktion / -methode angesehen:
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;
}
}
Das Runden auf 2 Dezimalstellen zur Darstellung eines Ergebnisses kann wie folgt durchgeführt werden:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Das val = 6.825
Ergebnis ist 6.83
wie erwartet.
Denn das val = 6.824999
Ergebnis ist 6.82
. Hier wird davon ausgegangen, dass die Berechnung genau ergab 6.824999
und die 7. Dezimalstelle Null ist.
Denn das val = 6.8249999
Ergebnis ist 6.83
. Die 7. Dezimalstelle 9
in diesem Fall bewirkt, dass die Round(val,6)
Funktion das erwartete Ergebnis liefert. In diesem Fall kann es eine beliebige Anzahl von nachgestellten 9
s geben.
Denn das val = 6.824999499999
Ergebnis ist 6.83
. Das Runden auf die 8. Dezimalstelle als erster Schritt, dh Round(val,8)
das Behandeln des einen bösen Falls, bei dem ein berechnetes Gleitkommaergebnis berechnet wird 6.8249995
, aber intern als dargestellt wird 6.824999499999...
.
Schließlich ist das Beispiel von der Frage ... val = 37.777779
ergibt 37.78
.
Dieser Ansatz könnte weiter verallgemeinert werden als:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
Dabei ist N die Genauigkeit, die für alle Zwischenberechnungen für Floats / Doubles beibehalten werden muss. Dies funktioniert auch bei negativen Werten. Ich weiß nicht, ob dieser Ansatz für alle Möglichkeiten mathematisch korrekt ist.
Einfacher C-Code zum Runden einer Zahl:
float n = 3.56;
printf("%.f", n);
Dies wird Folgendes ausgeben:
4
... oder Sie können es auf die altmodische Weise ohne Bibliotheken tun:
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;
Das natürlich, wenn Sie die zusätzlichen Informationen aus der Nummer entfernen möchten.
Diese Funktion nimmt die Zahl und Genauigkeit und gibt die gerundete Zahl zurück
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;
}
Es konvertiert die Gleitkommazahl in int, indem es den Punkt nach links verschiebt und nach der Bedingung größer als fünf sucht.
float
(unddouble
) keine dezimalen Gleitkommazahlen sind - sie sind binäre Gleitkommazahlen -, sodass das Runden auf Dezimalstellen bedeutungslos ist. Sie können die Ausgabe jedoch runden.