Wie drucke ich mit cout einen doppelten Wert mit voller Präzision?

331

Also habe ich die Antwort auf meine letzte Frage bekommen (ich weiß nicht, warum ich nicht daran gedacht habe). Ich habe eine doubleVerwendung gedruckt cout, die gerundet wurde, als ich sie nicht erwartet hatte. Wie kann ich einen coutAusdruck doublemit voller Präzision erstellen?

Jason Punyon
quelle

Antworten:

390

Sie können die Genauigkeit direkt einstellen std::coutund den std::fixedFormatbezeichner verwenden.

double d = 3.14159265358979;
cout.precision(17);
cout << "Pi: " << fixed << d << endl;

Sie können #include <limits>die maximale Präzision eines Floats oder Double erzielen.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Bill die Eidechse
quelle
46
Warum raten Sie ausdrücklich zur Verwendung fixed? Mit double h = 6.62606957e-34;, fixedgibt mir 0.000000000000000und scientificgibt aus 6.626069570000000e-34.
Arthur
36
Die Genauigkeit muss 17 sein (oder std :: numeric_limits <double> :: digits10 + 2), da beim Konvertieren von Dezimal zurück in die Binärdarstellung 2 zusätzliche Ziffern erforderlich sind, um sicherzustellen, dass der Wert auf denselben ursprünglichen Wert gerundet wird. Hier ist ein Artikel mit einigen Details: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher
8
Ist das wirklich die richtige Antwort? Wenn ich manuell eine hohe Zahl verwende, kann ich bis zu 51 Stellen mit ungefährem e ausdrucken, aber mit cout.precision(numeric_limits<double>::digits10 + 2);nur 16 ...
Assimilater
6
Für diejenigen, die suchen, wo es 17 Ziffern in der zitierten Zeitung @MikeFisher erwähnt, ist es unter Satz 15.
Emile Cormier
15
@MikeFisher Sie haben Recht, C ++ 11 führt einmax_digits10 , um dasselbe zu bezeichnen. Die Antwort wurde korrigiert, um dies widerzuspiegeln.
Legends2k
70

Verwendung std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Paul Beckingham
quelle
2
Gibt es eine Art MAX_PRECISION-Makro oder -Aufzählung oder etwas, das ich an std :: setPrecision übergeben kann?
Jason Punyon
2
std :: setprecision (15) für ein Double (ok oder 16), log_10 (2 ** 53) ~ = 15.9
user7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant
6
Sollte std::setprecision (17)doppelt sein, siehe Kommentare zu @ Bill The Lizard's Antwort.
Alec Jacobson
9
Damit std :: setprecision funktioniert, sollte #include <iomanip> enthalten sein.
user2262504
24

Folgendes würde ich verwenden:

std::cout << std::setprecision (std::numeric_limits<double>::digits10 + 1)
          << 3.14159265358979
          << std::endl;

Grundsätzlich enthält das Limits-Paket Merkmale für alle eingebauten Typen.
Eine der Eigenschaften für Gleitkommazahlen (float / double / long double) ist das Attribut digits10. Dies definiert die Genauigkeit (ich vergesse die genaue Terminologie) einer Gleitkommazahl in Basis 10.

Weitere Informationen zu anderen Attributen finden Sie unter: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
.

Martin York
quelle
12
Dieser Header wird benötigt, um zu verwenden std::setprecision(): #include <iomanip>
Martin Berger
es sollte std::numeric_limits<double>stattnumberic_limits<double>
niklasfi
2
Warum fügen Sie 1zu std::numeric_limits<double>::digits10?
Alessandro Jacopson
5
@LokiAstari Sie können max_digits10stattdessen C + 11 verwenden. Sehen Sie das .
Legends2k
1
@AlecJacobson Es sollte eher max_digits10nicht willkürlich sein digits10+2. Andernfalls wird im Falle float, long double, boost::multiprecision::float128wird dies nicht gelingen, da Sie bräuchten +3statt +2.
Ruslan
14

Der iostreams Weg ist irgendwie klobig. Ich bevorzuge die Verwendung, boost::lexical_castweil sie die richtige Präzision für mich berechnet. Und es ist auch schnell .

#include <string>
#include <boost/lexical_cast.hpp>

using boost::lexical_cast;
using std::string;

double d = 3.14159265358979;
cout << "Pi: " << lexical_cast<string>(d) << endl;

Ausgabe:

Pi: 3.14159265358979

Timothy003
quelle
In der Boost-Dokumentation heißt es: "Für Numerik mit einer entsprechenden Spezialisierung von std :: numeric_limits wählt die aktuelle Version jetzt eine passende Genauigkeit." Dies scheint der einfachste Weg zu sein, um die maximale Präzision zu erreichen. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Bei voller Genauigkeit gehe ich davon aus, dass die Genauigkeit genau genug ist, um die beste Annäherung an den beabsichtigten Wert zu zeigen, aber es sollte darauf hingewiesen werden, dass doubledie Verwendung unter Verwendung der Basis 2-Darstellung gespeichert wird und die Basis 2 nicht so trivial wie 1.1genau darstellen kann. Die einzige Möglichkeit, die volle Genauigkeit des tatsächlichen Doppels (ohne Abrundungsfehler) zu erhalten, besteht darin, die Binärbits (oder Hex-Nybbles) auszudrucken. Eine Möglichkeit, dies zu tun, besteht darin, das doublein a zu schreiben unionund dann den ganzzahligen Wert der Bits auszudrucken.

union {
    double d;
    uint64_t u64;
} x;
x.d = 1.1;
std::cout << std::hex << x.u64;

Dies gibt Ihnen die 100% genaue Genauigkeit des Doppels ... und ist absolut unlesbar, da Menschen das IEEE-Doppelformat nicht lesen können! Wikipedia hat eine gute Beschreibung der Interpretation der Binärbits.

In neuerem C ++ können Sie dies tun

std::cout << std::hexfloat << 1.1;
Mark Lakata
quelle
10

So zeigen Sie ein Double mit voller Präzision an:

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::setprecision(precision) << d << std::endl;

Dies zeigt an:

100.0000000000005


max_digits10 ist die Anzahl der Stellen, die erforderlich sind, um alle unterschiedlichen Doppelwerte eindeutig darzustellen. max_digits10 steht für die Anzahl der Stellen vor und nach dem Dezimalpunkt.


Verwenden Sie set_precision (max_digits10) nicht mit std :: fixed.
Bei fester Notation legt set_precision () die Anzahl der Stellen erst nach dem Dezimalpunkt fest. Dies ist falsch, da max_digits10 die Anzahl der Stellen vor und nach dem Dezimalpunkt darstellt.

double d = 100.0000000000005;
int precision = std::numeric_limits<double>::max_digits10;
std::cout << std::fixed << std::setprecision(precision) << d << std::endl;

Dies zeigt ein falsches Ergebnis an:

100.00000000000049738

Hinweis: Header-Dateien erforderlich

#include <iomanip>
#include <limits>
Daniel Laügt
quelle
4
Dies geschieht, weil 100.0000000000005nicht genau als dargestellt wird double. (Es mag so aussehen, als ob es sollte, aber es tut es nicht, weil es normalisiert wird , dh seine binäre Darstellung). Um dies zu sehen, versuchen Sie : 100.0000000000005 - 100. Wir bekommen 4.973799150320701e-13.
Evgeni Sergeev
9

Wie drucke ich einen doubleWert mit cout mit voller Präzision?

Verwenden hexfloatoder
verwenden scientificund stellen Sie die Präzision ein

std::cout.precision(std::numeric_limits<double>::max_digits10 - 1);
std::cout << std::scientific <<  1.0/7.0 << '\n';

// C++11 Typical output
1.4285714285714285e-01

Zu viele Antworten sprechen nur eine von 1) Basis 2) festem / wissenschaftlichem Layout oder 3) Präzision an. Zu viele präzise Antworten liefern nicht den richtigen Wert. Daher diese Antwort auf eine alte Frage.

  1. Welche Basis?

A doublewird sicherlich mit Basis 2 codiert. Ein direkter Ansatz mit C ++ 11 besteht darin, mit zu drucken std::hexfloat.
Wenn eine nicht dezimale Ausgabe akzeptabel ist, sind wir fertig.

std::cout << "hexfloat: " << std::hexfloat << exp (-100) << '\n';
std::cout << "hexfloat: " << std::hexfloat << exp (+100) << '\n';
// output
hexfloat: 0x1.a8c1f14e2af5dp-145
hexfloat: 0x1.3494a9b171bf5p+144

  1. Sonst: fixedoder scientific?

A doubleist ein Gleitkommatyp , kein Festkomma .

Verwenden Sie nicht , std::fixedda dies nicht so klein doublewie alles andere als drucken kann 0.000...000. Im Großen doubleund Ganzen werden viele Ziffern gedruckt, möglicherweise Hunderte fragwürdiger Informationen.

std::cout << "std::fixed: " << std::fixed << exp (-100) << '\n';
std::cout << "std::fixed: " << std::fixed << exp (+100) << '\n';
// output
std::fixed: 0.000000
std::fixed: 26881171418161356094253400435962903554686976.000000 

Um mit voller Präzision zu drucken, verwenden Sie zuerst std::scientific"Gleitkommawerte in wissenschaftlicher Notation schreiben". Beachten Sie, dass der Standardwert von 6 Stellen, nachdem der Dezimalpunkt, ein unzureichender Betrag, im nächsten Punkt behandelt wird.

std::cout << "std::scientific: " << std::scientific << exp (-100) << '\n';  
std::cout << "std::scientific: " << std::scientific << exp (+100) << '\n';
// output
std::scientific: 3.720076e-44
std::scientific: 2.688117e+43

  1. Wie genau (wie viele Ziffern insgesamt)?

Eine doubleunter Verwendung der Binärbasis 2 codierte Codierung codiert die gleiche Genauigkeit zwischen verschiedenen Potenzen von 2. Dies sind häufig 53 Bit.

[1.0 ... 2.0) es gibt 2 53 verschiedene double,
[2.0 ... 4.0) es gibt 2 53 verschiedene double,
[4.0 ... 8.0) es gibt 2 53 verschiedene double,
[8.0 ... 10.0) es gibt 2 / 8 * 2 53 verschiedene double.

Doch wenn Code druckt in dezimal mit Nsignifikanten Stellen, die Anzahl der Kombinationen [1.0 ... 10.0) ist 9/10 * 10 N .

Unabhängig von der Ngewählten (Genauigkeit) erfolgt keine Eins-zu-Eins-Zuordnung zwischen doubleund Dezimaltext. Wenn ein Fix Nausgewählt wird, ist er manchmal für bestimmte doubleWerte etwas mehr oder weniger als wirklich erforderlich . Wir könnten bei zu wenigen ( a)unten) oder zu vielen ( b)unten) Fehler machen .

3 Kandidat N:

a) Verwenden Sie ein Nso, wenn Sie aus doubleText-Text konvertieren, erhalten wir für alle den gleichen Text double.

std::cout << dbl::digits10 << '\n';
// Typical output
15

b) Verwenden Sie ein Nso, wenn Sie von double-text- konvertieren, doublewir kommen doublefür alle gleich double.

// C++11
std::cout << dbl::max_digits10 << '\n';
// Typical output
17

Wenn max_digits10nicht verfügbar, beachten Sie, dass digits10 + 2 <= max_digits10 <= digits10 + 3wir aufgrund der Attribute Basis 2 und Basis 10 verwenden könnendigits10 + 3 , dass genügend Dezimalstellen gedruckt werden.

c) Verwenden Sie eine N, die mit dem Wert variiert.

Dies kann nützlich sein, wenn Code minimalen Text ( N == 1) oder den genauen Wert von a double( N == 1000-ishim Fall von denorm_min) anzeigen möchte . Da dies jedoch "Arbeit" ist und wahrscheinlich nicht das Ziel von OP ist, wird es beiseite gelegt.


Es wird normalerweise b) verwendet, um "einen doubleWert mit voller Genauigkeit zu drucken ". Einige Anwendungen ziehen es möglicherweise vor, a) Fehler zu machen, wenn nicht zu viele Informationen bereitgestellt werden.

Legt mit fest .scientific, wie .precision()viele Stellen nach dem Dezimalpunkt 1 + .precision()gedruckt werden sollen , damit die Stellen gedruckt werden. Code benötigt max_digits10insgesamt Ziffern und wird daher .precision()mit a aufgerufen max_digits10 - 1.

typedef std::numeric_limits< double > dbl;
std::cout.precision(dbl::max_digits10 - 1);
std::cout << std::scientific <<  exp (-100) << '\n';
std::cout << std::scientific <<  exp (+100) << '\n';
// Typical output
3.7200759760208361e-44
2.6881171418161356e+43
//1234567890123456  17 total digits

Ähnliche C-Frage

chux - Monica wieder einsetzen
quelle
Gute Antwort! Ein paar Anmerkungen: Sie haben Recht, dass precision()die Anzahl der Dezimalstellen für den wissenschaftlichen Modus festgelegt wird. Ohne Angabe scientificwird die Gesamtzahl der Ziffern ohne Exponenten festgelegt. Abhängig von Ihrem Zahlenwert erhalten Sie möglicherweise immer noch wissenschaftliche Ergebnisse, aber dann erhalten Sie möglicherweise auch weniger Ziffern als angegeben. Beispiel: Die cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"Ergebnisse für printfkönnen unterschiedlich sein. Verwirrende Dinge, auf die man achten sollte.
Simpleton
Für die Nachwelt ist hier die erforderliche Pufferlänge für die garantierte exakte Zeichenfolgendarstellung aller Doppelzahlen im wissenschaftlichen Modus mit printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);Die zusätzlichen Zeichen sind für: Vorzeichen, Dezimalpunkt, nachgestellte Null, e [+ | -], 3 Stellen für den Exponenten ( DBL_MAX_10_EXP = 308). Daher beträgt die Gesamtzahl der erforderlichen Zeichen 25.
Simpleton
Ich kann meinen ersten Kommentar nicht bearbeiten, also gehen wir noch einmal: Ein weiteres Problem im wissenschaftlichen Modus ist, dass möglicherweise keine Exponentialausgabe verwendet wird oder sogar überhaupt keine Gleitkommaausgabe verwendet wird. Das heißt, es wird 1.0 als "1" ausgegeben, was in einem Serialisierungs- / Deserialisierungskontext ein Problem sein kann. Sie können es erzwingen, einen Dezimalpunkt auszugeben, indem Sie "% #. * G" verwenden. Dies hat jedoch den Nachteil, dass es eine Reihe von nachgestellten Nullen hinzufügt, was ohne das # ...
Simpleton
3
printf("%.12f", M_PI);

% .12f bedeutet Gleitkomma mit einer Genauigkeit von 12 Stellen.

Maister
quelle
11
Dies ist nicht "using cout".
Johnsyweb
2
12 Stellen ist nicht "volle Präzision"
Roland Illig
0

Am tragbarsten ...

#include <limits>

using std::numeric_limits;

    ...
    cout.precision(numeric_limits<double>::digits10 + 1);
    cout << d;

quelle
16
Ich bin gespannt: warum die "+1"?
Éric Malenfant
Habe das gerade abgelehnt.
stephanmg
0

Mit ostream :: präzise (int)

cout.precision( numeric_limits<double>::digits10 + 1);
cout << M_PI << ", " << M_E << endl;

wird nachgeben

3.141592653589793, 2.718281828459045

Warum Sie "+1" sagen müssen Ich habe keine Ahnung, aber die zusätzliche Ziffer, die Sie daraus erhalten, ist korrekt.

Jann
quelle
3
numeric_limits <unsigned char> :: digits10 ist gleich 2. Da es eine beliebige Dezimalzahl aus zwei Ziffern 0..99 enthalten kann. Es kann auch 255 enthalten, aber nicht 256, 257 ... 300 usw. Deshalb ist digits10 nicht 3! Ich denke, "+1" wird hinzugefügt, um so etwas zu überwinden.
Dmitriy Yurchenko
0

Dies zeigt den Wert bis zu zwei Dezimalstellen nach dem Punkt an.

#include <iostream>
#include <iomanip>

double d = 2.0;
int n = 2;
cout << fixed << setprecison(n) << d;

Siehe hier: Festkommanotation

std :: behoben

Feste Gleitkommanotation verwenden Setzt das Floatfield-Format-Flag für den str-Stream auf fest.

Wenn floatfield auf fest gesetzt ist, werden Gleitkommawerte in Festkommanotation geschrieben: Der Wert wird mit genau so vielen Stellen im Dezimalteil dargestellt, wie durch das Genauigkeitsfeld (Genauigkeit) angegeben, und ohne Exponententeil.

std :: setprecision

Festlegen der Dezimalgenauigkeit Legt die Dezimalgenauigkeit fest, die zum Formatieren von Gleitkommawerten bei Ausgabeoperationen verwendet werden soll.

Wenn Sie mit dem IEEE-Standard zur Darstellung der Gleitkommazahlen vertraut sind, wissen Sie, dass es unmöglich ist, Gleitkommazahlen außerhalb des Geltungsbereichs des Standards mit voller Genauigkeit anzuzeigen , das heißt, dies führt immer dazu eine Rundung des realen Wertes.

Sie müssen zuerst prüfen, ob der Wert innerhalb des Bereichs liegt. Wenn ja, verwenden Sie:

cout << defaultfloat << d ;

std :: defaultfloat

Standard-Gleitkommanotation verwenden Setzt das Floatfield-Format-Flag für den str-Stream auf defaultfloat.

Wenn floatfield auf defaultfloat gesetzt ist, werden Gleitkommawerte mit der Standardnotation geschrieben: Die Darstellung verwendet so viele aussagekräftige Ziffern wie erforderlich bis zur Dezimalgenauigkeit (Genauigkeit) des Streams und zählt sowohl die Ziffern vor als auch nach dem Dezimalpunkt (falls vorhanden) ).

Dies ist auch das Standardverhalten von cout, was bedeutet, dass Sie es nicht explizit verwenden.

emmmphd
quelle