Ich stolpere immer wieder über die Formatbezeichner für die Funktionsfamilie printf(). Was ich möchte, ist in der Lage zu sein, ein Double (oder Float) mit einer maximal gegebenen Anzahl von Ziffern nach dem Dezimalkomma zu drucken. Wenn ich verwende:
Gleitkomma-Ungenauigkeit bedeutet wirklich, dass Sie die Rundung selbst vornehmen sollten. Nehmen Sie eine Variante der Antwort von R und Juha (die die abschließenden Nullen nicht ganz verarbeiten) und korrigieren Sie sie.
– wnoise
9. Februar 2011 um 0:56 Uhr
paxdiablo
Das geht mit dem normalen nicht printf Formatbezeichner. Das nächste, was Sie bekommen könnten, wäre:
Was du kann tun ist zu sprintf("%.20g") die Zahl in einen String-Puffer und bearbeiten Sie dann den String so, dass er nur N Zeichen nach dem Dezimalkomma enthält.
Angenommen, Ihre Nummer befindet sich in der Variablen num, entfernt die folgende Funktion alle bis auf die erste N Dezimalstellen, entfernen Sie dann die nachgestellten Nullen (und den Dezimalpunkt, wenn sie alle Nullen waren).
char str[50];
sprintf (str,"%.20g",num); // Make the number.
morphNumericString (str, 3);
: :
void morphNumericString (char *s, int n) {
char *p;
int count;
p = strchr (s,'.'); // Find decimal point, if any.
if (p != NULL) {
count = n; // Adjust for more or less decimals.
while (count >= 0) { // Maximum decimals allowed.
count--;
if (*p == '\0') // If there's less than desired.
break;
p++; // Next character.
}
*p-- = '\0'; // Truncate string.
while (*p == '0') // Remove trailing zeros.
*p-- = '\0';
if (*p == '.') { // If all decimals were zeros, remove ".".
*p = '\0';
}
}
}
Wenn Sie mit dem Abschneideaspekt nicht zufrieden sind (was sich ändern würde 0.12399 hinein 0.123 anstatt es zu runden 0.124), können Sie die bereits von bereitgestellten Rundungsfunktionen verwenden printf. Sie müssen die Zahl nur vorher analysieren, um die Breiten dynamisch zu erstellen, und diese dann verwenden, um die Zahl in eine Zeichenfolge umzuwandeln:
#include <stdio.h>
void nDecimals (char *s, double d, int n) {
int sz; double d2;
// Allow for negative.
d2 = (d >= 0) ? d : -d;
sz = (d >= 0) ? 0 : 1;
// Add one for each whole digit (0.xx special case).
if (d2 < 1) sz++;
while (d2 >= 1) { d2 /= 10.0; sz++; }
// Adjust for decimal point and fractionals.
sz += 1 + n;
// Create format string then use it.
sprintf (s, "%*.*f", sz, n, d);
}
int main (void) {
char str[50];
double num[] = { 40, 359.01335, -359.00999,
359.01, 3.01357, 0.111111111, 1.1223344 };
for (int i = 0; i < sizeof(num)/sizeof(*num); i++) {
nDecimals (str, num[i], 3);
printf ("%30.20f -> %s\n", num[i], str);
}
return 0;
}
Der springende Punkt von nDecimals() in diesem Fall ist es, die Feldbreiten korrekt zu berechnen und dann die Zahl mit einem darauf basierenden Formatstring zu formatieren. Der Prüfstand main() zeigt dies in Aktion:
Sobald Sie den korrekt gerundeten Wert haben, können Sie diesen erneut an übergeben morphNumericString() um nachgestellte Nullen zu entfernen, indem Sie einfach Folgendes ändern:
(Oder anrufen morphNumericString am Ende von nDecimals aber in diesem Fall würde ich wahrscheinlich nur die beiden zu einer Funktion kombinieren), und Sie erhalten am Ende:
Beachten Sie, dass diese Antwort davon ausgeht, dass die Dezimalstelle ist . In manchen Gegenden ist die Dezimalstelle tatsächlich a , Komma.
– Benutzer609020
9. Februar 2011 um 0:32 Uhr
Hier ist tatsächlich ein kleiner Tippfehler – p = strchr (str,’.’); sollte eigentlich p = strchr (s,’.’) sein; Um die Funktion param anstelle der globalen var zu verwenden.
– n3wtz
2. Mai 2011 um 14:15 Uhr
Die Verwendung zu vieler Ziffern kann zu unerwarteten Ergebnissen führen … zum Beispiel “%.20g” mit 0,1 zeigt Müll an. Was Sie tun müssen, wenn Sie die Benutzer nicht überraschen möchten, ist, mit beispielsweise 15 Ziffern zu beginnen und weiter zu inkrementieren, bis atof gibt den gleichen Wert zurück.
– 6502
21. Juli 2015 um 16:36 Uhr
@ 6502, das liegt daran, dass es in IEEE754 keine 0,1 gibt 🙂 Dies wird jedoch (zumindest für diesen Wert) seit dem Ergebnis kein Problem verursachen 0.10000000000000000555 wird sein 0.1 beim Zurückziehen. Das Problem kann auftreten, wenn Sie einen Wert haben, der leicht unter der nächsten Darstellung von liegt 42.1 war 42.099999999314159. Wenn Sie das wirklich handhaben wollten, müssten Sie wahrscheinlich basierend auf der letzten entfernten Ziffer runden, anstatt abzuschneiden.
– paxdiablo
22. Juli 2015 um 1:20 Uhr
@paxdiablo: Es ist nicht erforderlich, dynamisch eine Formatzeichenfolge für zu erstellen printf-ähnliche Funktionen, da sie bereits dynamische Parameter unterstützen (* Sonderzeichen): zum Beispiel printf("%*.*f", total, decimals, x); gibt eine Zahl mit der dynamisch angegebenen Gesamtfeldlänge und Dezimalstellen aus.
– 6502
31. August 2015 um 5:57 Uhr
Tomalak
Um die abschließenden Nullen loszuwerden, sollten Sie das Format “%g” verwenden:
float num = 1.33;
printf("%g", num); //output: 1.33
Nachdem die Frage etwas präzisiert wurde, wurde nicht nur das Unterdrücken von Nullen gefragt, sondern auch die Begrenzung der Ausgabe auf drei Nachkommastellen. Ich denke, das geht nicht allein mit sprintf-Formatzeichenfolgen. Wie Pax Diablo betonte, wäre eine String-Manipulation erforderlich.
@Tomalak: Das macht nicht das, was ich will. Ich möchte eine maximale Anzahl von Nachkommastellen angeben können. Dies ist: Ich möchte, dass 1,3347 “1,335” und 1,3397 “1,34” gedruckt werden. @xtofl: Ich hatte das bereits überprüft, aber ich kann immer noch keine Antwort auf mein Problem sehen
– Gorpik
10. November 2008 um 12:55 Uhr
Der Autor möchte Stil „f“, nicht Stil „e“. ‘g’ kann den Stil ‘e’ verwenden: Aus der Dokumentation: Der Stil e wird verwendet, wenn der Exponent aus seiner Konvertierung kleiner als -4 oder größer oder gleich der Genauigkeit ist.
– Julien Palard
4. Juni 2013 um 14:58 Uhr
Juha
Ich mag die Antwort von R. leicht abgeändert:
float f = 1234.56789;
printf("%d.%.0f", f, 1000*(f-(int)f));
‘1000’ bestimmt die Genauigkeit.
Power auf die 0,5 Rundung.
BEARBEITEN
Ok, diese Antwort wurde ein paar Mal bearbeitet und ich habe den Überblick verloren, was ich vor ein paar Jahren gedacht habe (und ursprünglich hat es nicht alle Kriterien erfüllt). Hier ist also eine neue Version (die alle Kriterien erfüllt und negative Zahlen korrekt behandelt):
Die maximale Anzahl der Dezimalstellen hinter der Null ist festgelegt
Nachgestellte Nullen werden entfernt
es macht es mathematisch richtig (richtig?)
funktioniert (jetzt) auch, wenn die erste Dezimalstelle Null ist
Leider ist diese Antwort ein Zweizeiler, da sprintf gibt den String nicht zurück.
Dies scheint jedoch keine nachgestellten Nullen zu kürzen? So etwas wie 1,1000 kann er gerne ausspucken.
– Dekan J
4. September 2013 um 2:24 Uhr
Die Frage und meine Antwort sind nicht mehr die Originale … Ich werde später nachsehen.
– Juha
9. September 2013 um 7:11 Uhr
Fehlgeschlagene Testfälle: 1.012 mit Formatierer „%.2g“ ergibt 1.012 statt 1.01.
– wtl
13. Februar 2014 um 13:54 Uhr
@wtl Schöner Fang. Jetzt behoben.
– Juha
14. Februar 2014 um 20:32 Uhr
@Jeroen: Ich denke, die Floats versagen auch irgendwann, wenn die Zahlen größer werden … aber im Grunde sollte die Typumwandlung in long, long long oder int64 funktionieren.
– Juha
10. Oktober 2014 um 18:41 Uhr
Warum nicht einfach so?
double f = 359.01335;
printf("%g", round(f * 1000.0) / 1000.0);
DaveR
Ich suche die Zeichenfolge (beginnend ganz rechts) nach dem ersten Zeichen im Bereich 1 zu 9 (ASCII-Wert 49–57) dann null (einstellen 0) jedes Zeichen rechts davon – siehe unten:
void stripTrailingZeros(void) {
//This finds the index of the rightmost ASCII char[1-9] in array
//All elements to the left of this are nulled (=0)
int i = 20;
unsigned char char1 = 0; //initialised to ensure entry to condition below
while ((char1 > 57) || (char1 < 49)) {
i--;
char1 = sprintfBuffer[i];
}
//null chars left of i
for (int j = i; j < 20; j++) {
sprintfBuffer[i] = 0;
}
}
Sri Harsha Chilakapati
Was ist mit so etwas (kann Rundungsfehler und Probleme mit negativen Werten haben, die debuggt werden müssen, als Übung für den Leser):
Es ist leicht programmatisch, aber zumindest lässt es Sie keine Zeichenfolgen manipulieren.
nwellnhof
Einige der hoch bewerteten Lösungen schlagen vor %g Konvertierungsbezeichner von printf. Das ist falsch, weil es Fälle gibt, in denen %g erzeugt wissenschaftliche Notation. Andere Lösungen verwenden Mathematik, um die gewünschte Anzahl von Dezimalstellen zu drucken.
Ich denke, die einfachste Lösung ist die Verwendung sprintf mit dem %f Konvertierungsspezifizierer und um nachgestellte Nullen und möglicherweise ein Dezimalkomma manuell aus dem Ergebnis zu entfernen. Hier ist eine C99-Lösung:
#include <stdio.h>
#include <stdlib.h>
char*
format_double(double d) {
int size = snprintf(NULL, 0, "%.3f", d);
char *str = malloc(size + 1);
snprintf(str, size + 1, "%.3f", d);
for (int i = size - 1, end = size; i >= 0; i--) {
if (str[i] == '0') {
if (end == i + 1) {
end = i;
}
}
else if (str[i] == '.') {
if (end == i + 1) {
end = i;
}
str[end] = '\0';
break;
}
}
return str;
}
Beachten Sie, dass die für Ziffern und das Dezimaltrennzeichen verwendeten Zeichen vom aktuellen Gebietsschema abhängen. Der obige Code geht von einem Gebietsschema in C oder US-Englisch aus.
−0,00005 und 3 Dezimalstellen ergeben “−0”, was erwünscht sein kann oder nicht. Vielleicht machen Sie “3” auch zu einem Parameter für die Bequemlichkeit anderer?
– Delfin
13. Juli 2020 um 21:49 Uhr
14232500cookie-checkVermeiden Sie nachgestellte Nullen in printf()yes
Gleitkomma-Ungenauigkeit bedeutet wirklich, dass Sie die Rundung selbst vornehmen sollten. Nehmen Sie eine Variante der Antwort von R und Juha (die die abschließenden Nullen nicht ganz verarbeiten) und korrigieren Sie sie.
– wnoise
9. Februar 2011 um 0:56 Uhr