Wie kann ich einen Gleitkommawert (z. B. 37.777779) auf zwei Dezimalstellen (37.78) in C runden?
c
floating-point
decimal
Sakib Arifin
quelle
quelle
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.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: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 .
quelle
doubles
irgendwie auch zum Laufen bringen? Scheint nicht den Job zu machen, den ich will :( (mitfloor
undceil
).Verwenden von % .2f in printf. Es werden nur 2 Dezimalstellen gedruckt.
Beispiel:
Ausgabe:
quelle
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:
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.
quelle
printf("%.*f", (int)precision, (double)number);
Wie wäre es damit:
quelle
Wenn Sie in C-String schreiben möchten:
quelle
Es gibt keine Möglichkeit, a
float
auf einen anderen zu runden ,float
da die Rundungfloat
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" .quelle
float
auf n Dezimalstellen runden können und dann erwarten, dass das Ergebnis immer n Dezimalstellen hat. Sie werden immer noch eine bekommenfloat
, nur nicht die, die Sie erwartet haben.Wenn Sie C ++ verwenden, können Sie auch einfach eine Funktion wie die folgende erstellen:
Sie können dann mit Code wie diesem ein beliebiges Double
myDouble
mitn
Stellen nach dem Dezimalpunkt ausgeben :quelle
Sie können weiterhin verwenden:
Beispiel:
quelle
In C ++ (oder in C mit Casts im C-Stil) können Sie die folgende Funktion erstellen:
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.
quelle
Verwenden Sie dazu immer die
printf
Funktionsfamilie. Selbst wenn Sie den Wert als Float erhalten möchten, verwenden Sie am bestensnprintf
den gerundeten Wert als Zeichenfolge und analysieren ihn anschließend mitatof
: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.
Sie sehen diese Ausgabe:
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 Verwendunground(100 * x) / 100
gibt uns 0,02, was falsch ist. Warum? Denn100 * 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: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:
Hoppla. Unsere
snprintf
Methode liefert wieder das richtige Ergebnis, aber der Ansatz "Multiplizieren, Runden, Teilen" schlägt fehl. Das ist , weil der mathematisch korrekte Wert8631192423766613.0 * 100
,863119242376661300.0
ist nicht exakt darstellbar als Doppel; der nächste Wert ist863119242376661248.0
. Wenn Sie das durch 100 teilen, erhalten Sie8631192423766612.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 Siesnprintf
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 .quelle
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
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 hinaus0.01
ist es am schwierigsten, in allen "halben" Fällen genau auf das nächste zu runden .Obwohl "1.115" zwischen 1.11 und 1.12 "auf halbem Weg" liegt,
float
ist1.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 Wertfloat
von1.120000004768...
"1.125" ist "auf halbem Weg" zwischen 1.12 und 1.13, wenn in konvertiert,
float
ist der Wert genau1.125
und ist "auf halbem Weg". Es rundet in Richtung 1,13, weil es gleichmäßige Regeln gibt, und rundet auf das nächstefloat
von1.129999995232...
Obwohl "1.135" zwischen 1.13 und 1.14 "auf halbem Weg" liegt,
float
ist1.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 Wertfloat
von1.129999995232...
Wenn Code verwendet wird
Obwohl „1,135“ ist „ auf halbem Wege“ zwischen 1,13 und 1,14, wenn umgewandelt
float
, der Wert1.134999990463...
und ist nicht mehr „ auf halbem Wege“, aber näher an 1,13 aber falsch Rundenfloat
der1.139999985695...
aufgrund der begrenzteren Genauigkeitfloat
vs.double
. Dieser falsche Wert kann abhängig von den Codierungszielen als korrekt angesehen werden.quelle
Ich habe dieses Makro zum Runden von Float-Zahlen erstellt. Fügen Sie es in Ihren Header / Sein der Datei ein
Hier ist ein Beispiel:
x entspricht 3,14 :)
quelle
Hier
n
ist die Anzahl der DezimalstellenBeispiel:
quelle
dval
es riesig ist 3) das Unheimlicheif
/else
Blockieren, bei dem Sie in jedem Zweig genau dasselbe tun und 4) die überkomplizierte Verwendungsprintf
zum Erstellen des Formatbezeichners für einen zweitensprintf
Aufruf; Es ist einfacher, nur.*
den doppelten Wert und die Anzahl der Dezimalstellen als Argumente für denselbensprintf
Aufruf zu verwenden und zu übergeben.Code-Definition:
Ergebnisse :
quelle
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:
Das Runden auf 2 Dezimalstellen zur Darstellung eines Ergebnisses kann wie folgt durchgeführt werden:
Das
val = 6.825
Ergebnis ist6.83
wie erwartet.Denn das
val = 6.824999
Ergebnis ist6.82
. Hier wird davon ausgegangen, dass die Berechnung genau ergab6.824999
und die 7. Dezimalstelle Null ist.Denn das
val = 6.8249999
Ergebnis ist6.83
. Die 7. Dezimalstelle9
in diesem Fall bewirkt, dass dieRound(val,6)
Funktion das erwartete Ergebnis liefert. In diesem Fall kann es eine beliebige Anzahl von nachgestellten9
s geben.Denn das
val = 6.824999499999
Ergebnis ist6.83
. Das Runden auf die 8. Dezimalstelle als erster Schritt, dhRound(val,8)
das Behandeln des einen bösen Falls, bei dem ein berechnetes Gleitkommaergebnis berechnet wird6.8249995
, aber intern als dargestellt wird6.824999499999...
.Schließlich ist das Beispiel von der Frage ...
val = 37.777779
ergibt37.78
.Dieser Ansatz könnte weiter verallgemeinert werden als:
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.
quelle
Einfacher C-Code zum Runden einer Zahl:
Dies wird Folgendes ausgeben:
quelle
... oder Sie können es auf die altmodische Weise ohne Bibliotheken tun:
Das natürlich, wenn Sie die zusätzlichen Informationen aus der Nummer entfernen möchten.
quelle
Diese Funktion nimmt die Zahl und Genauigkeit und gibt die gerundete Zahl zurück
Es konvertiert die Gleitkommazahl in int, indem es den Punkt nach links verschiebt und nach der Bedingung größer als fünf sucht.
quelle