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 .
doublesirgendwie auch zum Laufen bringen? Scheint nicht den Job zu machen, den ich will :( (mit floorund ceil).
Verwenden von % .2f in printf. Es werden nur 2 Dezimalstellen gedruckt.
Beispiel:
printf("%.2f", 37.777779);
Ausgabe:
37.77
floatReichweitenverluste auftreten, val * 100die ü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 floatauf einen anderen zu runden , floatda die Rundung floatmö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 floatmithilfe einer Formatzeichenfolgenfunktion "runden" .
floatauf 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 myDoublemit nStellen 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 printfFunktionsfamilie. Selbst wenn Sie den Wert als Float erhalten möchten, verwenden Sie am besten snprintfden 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 snprintfgibt uns dieses Ergebnis, aber die Verwendung round(100 * x) / 100gibt uns 0,02, was falsch ist. Warum? Denn 100 * xgibt 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 * 100und / 100nicht 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 snprintfMethode 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.0ist 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 roundfzum Runden auf eine Anzahl von Dezimalstellen fehlerhaft ist und dass Sie snprintfstattdessen 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 floatImplementierung sind Zahlen, die auf halbem Weg zu sein scheinen, nicht. als Gleitkomma ist typischerweise Basis-2-orientiert. Darüber hinaus 0.01ist 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, floatist 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 floatvon1.120000004768...
"1.125" ist "auf halbem Weg" zwischen 1.12 und 1.13, wenn in konvertiert, floatist der Wert genau 1.125und ist "auf halbem Weg". Es rundet in Richtung 1,13, weil es gleichmäßige Regeln gibt, und rundet auf das nächste floatvon1.129999995232...
Obwohl "1.135" zwischen 1.13 und 1.14 "auf halbem Weg" liegt, floatist 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 floatvon1.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 floatder 1.139999985695...aufgrund der begrenzteren Genauigkeit floatvs. 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 nist 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
dvales riesig ist 3) das Unheimliche if/ elseBlockieren, bei dem Sie in jedem Zweig genau dasselbe tun und 4) die überkomplizierte Verwendung sprintfzum Erstellen des Formatbezeichners für einen zweiten sprintfAufruf; Es ist einfacher, nur .*den doppelten Wert und die Anzahl der Dezimalstellen als Argumente für denselben sprintfAufruf 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.825Ergebnis ist 6.83wie erwartet.
Denn das val = 6.824999Ergebnis ist 6.82. Hier wird davon ausgegangen, dass die Berechnung genau ergab 6.824999und die 7. Dezimalstelle Null ist.
Denn das val = 6.8249999Ergebnis ist 6.83. Die 7. Dezimalstelle 9in diesem Fall bewirkt, dass die Round(val,6)Funktion das erwartete Ergebnis liefert. In diesem Fall kann es eine beliebige Anzahl von nachgestellten 9s geben.
Denn das val = 6.824999499999Ergebnis 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.777779ergibt 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.