Gibt es einen printf
Breitenbezeichner, der auf einen Gleitkomma- Bezeichner angewendet werden kann, der die Ausgabe automatisch auf die erforderliche Anzahl signifikanter Stellen formatiert, sodass beim erneuten Einscannen der Zeichenfolge der ursprüngliche Gleitkommawert erfasst wird?
Angenommen, ich drucke a float
mit einer Genauigkeit von 2
Dezimalstellen:
float foobar = 0.9375;
printf("%.2f", foobar); // prints out 0.94
Wenn ich die Ausgabe scanne 0.94
, habe ich keine standardkonforme Garantie, dass ich den ursprünglichen 0.9375
Gleitkommawert zurückerhalte (in diesem Beispiel werde ich dies wahrscheinlich nicht tun).
Ich möchte eine Möglichkeit angeben printf
, den Gleitkommawert automatisch auf die erforderliche Anzahl von signifikanten Stellen zu drucken, um sicherzustellen, dass er auf den ursprünglichen Wert zurückgescannt werden kann, an den er übergeben wurde printf
.
Ich konnte einige der Makros in float.h
auf die maximale Breite ableiten zu passieren printf
, aber gibt es bereits ein Spezifizierer automatisch auf die erforderliche Anzahl von Druck signifikanten Stellen - oder zumindest auf die maximale Breite?
quelle
printf( "%f", val );
die bereits portabel, effizient und standardmäßig ist.double
. Wenn Siedouble
extrem groß werden (sehr weit von 1,0 entfernt), wird es im Dezimalbereich (Wertbereich kleiner als 1,0) tatsächlich weniger genau . Sie können hier also keine wirklich zufriedenstellende Antwort haben, da Ihre Frage eine falsche Annahme enthält (nämlich, dass allefloat
s /double
s gleich sind)Antworten:
Ich empfehle @Jens Gustedt hexadezimale Lösung: Verwenden Sie% a.
OP möchte "mit maximaler Genauigkeit (oder zumindest mit der höchstwertigen Dezimalstelle) drucken".
Ein einfaches Beispiel wäre, ein Siebtel wie folgt zu drucken:
Aber lasst uns tiefer graben ...
Mathematisch lautet die Antwort "0.142857 142857 142857 ...", aber wir verwenden Gleitkommazahlen mit endlicher Genauigkeit. Nehmen wir an, IEEE 754 Binär mit doppelter Genauigkeit . Also die
OneSeventh = 1.0/7.0
Ergebnisse im Wert unten. Ebenfalls gezeigt sind die vorhergehenden und folgenden darstellbarendouble
Gleitkommazahlen.Das Drucken der exakten Dezimaldarstellung von a
double
hat nur begrenzte Verwendungsmöglichkeiten.C hat 2 Makrofamilien
<float.h>
, die uns helfen.Der erste Satz gibt die Anzahl der signifikanten Stellen an, die in einer Zeichenfolge in Dezimalzahl gedruckt werden sollen. Wenn Sie die Zeichenfolge zurückscannen, erhalten Sie den ursprünglichen Gleitkommawert. Es werden mit dem Mindestwert der C-Spezifikation und einem Beispiel- C11-Compiler angezeigt .
Der zweite Satz ist die Anzahl der signifikanten Stellen, die eine Zeichenfolge in einen Gleitkommawert gescannt und dann das FP gedruckt werden kann, wobei die gleiche Zeichenfolgenpräsentation beibehalten wird. Es werden mit dem Mindestwert der C-Spezifikation und einem Beispiel- C11-Compiler angezeigt . Ich glaube verfügbar vor C99.
Der erste Satz von Makros scheint das OP-Ziel von signifikanten Ziffern zu erfüllen . Dieses Makro ist jedoch nicht immer verfügbar.
Die "+ 3" war der Kern meiner vorherigen Antwort. Wenn man die Round-Trip-Konvertierungszeichenfolge-FP-Zeichenfolge kennt (Set # 2 Makros verfügbar C89), wie würde man die Ziffern für FP-String-FP bestimmen (Set # 1 Makros verfügbar nach C89)? Im Allgemeinen war Add 3 das Ergebnis.
Nun ist bekannt, über wie viele wichtige Ziffern gedruckt werden soll
<float.h>
.Um N signifikante Dezimalstellen zu drucken, kann man verschiedene Formate verwenden.
Mit
"%e"
ist das Genauigkeitsfeld die Anzahl der Stellen nach der führenden Ziffer und dem Dezimalpunkt. So- 1
ist es in Ordnung. Hinweis: Dies-1
ist nicht in der Initialeint Digs = DECIMAL_DIG;
Mit
"%f"
ist das Genauigkeitsfeld die Anzahl der Stellen nach dem Dezimalpunkt. Für eine Zahl wieOneSeventh/1000000.0
müssteOP_DBL_Digs + 6
man alle signifikanten Ziffern sehen.Hinweis: Viele sind es gewohnt
"%f"
. Das zeigt 6 Stellen nach dem Dezimalpunkt an; 6 ist die Standardeinstellung für die Anzeige, nicht die Genauigkeit der Zahl.quelle
%f
6 ist."%f"
dem überhaupt gedruckt werden kann . Die Verwendung,"%e"
wie Sie gezeigt haben, ist natürlich ein rundum besserer Ansatz und effektiv eine anständige Antwort (obwohl sie möglicherweise nicht so gut ist wie die Verwendung"%a"
, wenn sie verfügbar ist, und natürlich"%a"
verfügbar sein sollte, wenn `DBL_DECIMAL_DIG verfügbar ist). Ich habe mir immer einen Formatbezeichner gewünscht, der immer genau auf die maximale Genauigkeit rundet (anstelle der fest codierten 6 Dezimalstellen).Die kurze Antwort zum verlustfreien Drucken von Gleitkommazahlen (so dass sie mit Ausnahme von NaN und Infinity auf genau dieselbe Zahl zurückgelesen werden können):
printf("%.9g", number)
.printf("%.17g", number)
.NICHT verwenden
%f
, da dies nur angibt, wie viele signifikante Stellen nach der Dezimalstelle stehen und kleine Zahlen abgeschnitten werden. Als Referenz finden Sie die magischen Zahlen 9 und 17, infloat.h
denenFLT_DECIMAL_DIG
und definiert sindDBL_DECIMAL_DIG
.quelle
%g
Spezifizierer erklären ?double
Werte knapp über0.1
:1.000_0000_0000_0000_2e-01
,1.000_0000_0000_0000_3e-01
müssen 17 Ziffern zu unterscheiden."%.16g"
ist unzureichend und"%.17g"
und"%.16e"
sind ausreichend. Die Details von%g
wurden von mir falsch in Erinnerung behalten.Wenn Sie nur an dem Bit (bzw. Hex-Muster) interessiert sind, können Sie das
%a
Format verwenden. Dies garantiert Ihnen:Ich muss hinzufügen, dass dies erst seit C99 verfügbar ist.
quelle
Nein, es gibt keinen solchen Druckbreitenbezeichner, um Gleitkommazahlen mit maximaler Genauigkeit zu drucken . Lassen Sie mich erklären, warum.
Die maximale Genauigkeit von
float
unddouble
ist variabel und hängt vom tatsächlichen Wert desfloat
oder abdouble
.Rückruf
float
unddouble
werden im Format sign.exponent.mantissa gespeichert. Dies bedeutet, dass für die Bruchkomponente für kleine Zahlen viel mehr Bits verwendet werden als für große Zahlen.Zum Beispiel
float
kann leicht zwischen 0,0 und 0,1 unterschieden werden.Hat
float
aber keine Ahnung vom Unterschied zwischen1e27
und1e27 + 0.1
.Dies liegt daran, dass die gesamte Genauigkeit (die durch die Anzahl der Mantissenbits begrenzt ist) für den großen Teil der Zahl links von der Dezimalstelle verbraucht wird.
Der
%.f
Modifikator gibt nur an, wie viele Dezimalwerte Sie bei der Formatierung aus der Gleitkommazahl drucken möchten . Die Tatsache, dass die verfügbare Genauigkeit von der Größe der Zahl abhängt, liegt bei Ihnen als Programmierer .printf
kann / kann das nicht für dich erledigen.quelle
float
, die Sie bereitstellen, und Sie behaupten, dass es so etwas nicht gibt (dh dass es keine gibtFLT_DIG
), was falsch ist.FLT_DIG
besagt, dass sie nichts bedeutet. Diese Antwort gibt an, dass die Anzahl der verfügbaren Dezimalstellen vom Wert im Float abhängt .Verwenden Sie einfach die Makros von
<float.h>
und den Konvertierungsspezifizierer mit variabler Breite (".*"
):quelle
printf("%." FLT_DIG "f\n", f);
%e
, nicht so gut für%f
: Nur wenn bekannt ist, dass der zu druckende Wert nahe liegt1.0
.%e
Gibt signifikante Ziffern für sehr kleine Zahlen aus und%f
nicht. zBx = 1e-100
.%.5f
Drucke0.00000
(ein totaler Präzessionsverlust).%.5e
druckt1.00000e-100
.FLT_DIG
wird auf den Wert definiert, für den es aus einem bestimmten Grund definiert ist. Wenn es 6 ist, liegt das daran, dassfloat
nicht mehr als 6 Stellen Genauigkeit gespeichert werden können. Wenn Sie es mit drucken%.7f
, hat die letzte Ziffer keine Bedeutung. Denken Sie nach, bevor Sie abstimmen.%.6f
ist nicht gleichwertig, weilFLT_DIG
es nicht immer 6 ist. Und wen interessiert die Effizienz? E / A ist bereits höllisch teuer, eine mehr oder weniger genaue Ziffer führt nicht zu einem Engpass.Ich
DBL_DECIMAL_DIG
führe ein kleines Experiment durch, um zu überprüfen, ob beim Drucken mit tatsächlich die binäre Darstellung der Zahl erhalten bleibt. Es stellte sich heraus, dass für die Compiler und C-Bibliotheken, die ich ausprobiert habe,DBL_DECIMAL_DIG
tatsächlich die Anzahl der erforderlichen Stellen erforderlich ist und das Drucken mit nur einer Stelle weniger ein erhebliches Problem darstellt.Ich führe dies mit dem Microsoft C-Compiler 19.00.24215.1 und der gcc-Version 7.4.0 20170516 (Debian 6.3.0-18 + deb9u1) aus. Die Verwendung einer Dezimalstelle weniger halbiert die Anzahl der Zahlen, die genau gleich sind. (Ich habe auch überprüft, dass
rand()
bei Verwendung tatsächlich etwa eine Million verschiedene Zahlen erzeugt werden.) Hier sind die detaillierten Ergebnisse.Microsoft C.
GCC
quelle
RAND_MAX == 32767
. Überlegen Sieu.s[j] = (rand() << 8) ^ rand();
oder ähnliches, umIn einem meiner Kommentare zu einer Antwort beklagte ich mich, dass ich schon lange eine Möglichkeit haben wollte, alle signifikanten Ziffern in einem Gleitkommawert in Dezimalform zu drucken, ähnlich wie in der Frage. Nun, ich habe mich endlich hingesetzt und es geschrieben. Es ist nicht ganz perfekt und dies ist ein Demo-Code, der zusätzliche Informationen druckt, aber meistens für meine Tests funktioniert. Bitte lassen Sie mich wissen, wenn Sie (dh jemand) eine Kopie des gesamten Wrapper-Programms wünschen, das es zum Testen antreibt.
quelle
Mein Wissen gibt es einen gut diffusen Algorithmus zu ermöglichen Ausgang auf die erforderliche Anzahl von signifikanten Stellen , so dass beim Scannen der Zeichenfolge wieder in, wird der ursprüngliche Fließkommawert erworben in
dtoa.c
geschrieben von Daniel Homosexuell, die verfügbar ist , hier auf Netlib (siehe auch das dazugehörige Papier ). Dieser Code wird zB in Python, MySQL, Scilab und vielen anderen verwendet.quelle