Come posso stampare un doppio valore con la massima precisione usando cout?


332

Quindi ho ottenuto la risposta alla mia ultima domanda (non so perché non ci abbia pensato). Stavo stampando un doubleuso coutarrotondato quando non me lo aspettavo. Come posso fare coutuna stampa doubleusando la massima precisione?

Risposte:


391

È possibile impostare la precisione direttamente std::coute utilizzare l' std::fixedidentificatore di formato.

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

È possibile #include <limits>ottenere la massima precisione di un galleggiante o doppio.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;

46
Perché consigli espressamente di usare fixed? Con double h = 6.62606957e-34;, fixedmi dà 0.000000000000000e scientificuscite 6.626069570000000e-34.
Arthur,

36
La precisione deve essere 17 (o std :: numeric_limits <double> :: digits10 + 2) perché sono necessarie 2 cifre extra quando si converte da decimale in rappresentazione binaria per assicurarsi che il valore sia arrotondato allo stesso valore originale. Ecco un documento con alcuni dettagli: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher,

8
È davvero la risposta giusta? Quando uso manualmente un numero alto, posso stampare fino a 51 cifre di e approssimativa, ma con cout.precision(numeric_limits<double>::digits10 + 2);ottengo solo 16 ....
Assimilater

6
Per coloro che guardano dove menziona 17 cifre nel documento citato da @MikeFisher, è sotto il Teorema 15.
Emile Cormier,

15
@MikeFisher Hai ragione, C ++ 11 introducemax_digits10 per indicare lo stesso. Risolto il problema con la risposta.
legends2k,

70

Utilizzare std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;

2
Esiste una specie di macro o enum MAX_PRECISION o qualcosa che posso passare a std :: setPrecision?
Jason Punyon

2
std :: setprecision (15) per un doppio (ok o 16), log_10 (2 ** 53) ~ = 15.9
user7116

14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant,

6
Dovrebbe essere std::setprecision (17)per doppio, vedere i commenti sulla risposta di @Bill The Lizard.
Alec Jacobson,

9
affinché std :: setprecision funzioni, #include <iomanip> deve essere incluso.
user2262504,

24

Ecco cosa vorrei usare:

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

Fondamentalmente il pacchetto limits ha tratti per tutti i tipi di build.
Uno dei tratti per i numeri in virgola mobile (float / double / long double) è l'attributo digits10. Questo definisce l'accuratezza (dimentico la terminologia esatta) di un numero in virgola mobile nella base 10.

Vedi: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Per dettagli su altri attributi.


12
Questa intestazione è necessaria per utilizzare std::setprecision(): #include <iomanip>
Martin Berger,

dovrebbe essere std::numeric_limits<double>invece dinumberic_limits<double>
niklasfi il

2
Perché aggiungi 1a std::numeric_limits<double>::digits10?
Alessandro Jacopson,

5
@LokiAstari Puoi usare invece C + 11 max_digits10. Vedere questo .
legends2k,

1
@AlecJacobson Dovrebbe essere piuttosto max_digits10, non un po 'arbitrario digits10+2. In caso contrario, nel caso di float, long double, boost::multiprecision::float128questo fallirà, dal momento che ci avresti bisogno +3invece di +2.
Ruslan,

14

La via degli iostreams è piuttosto goffa. Preferisco usareboost::lexical_cast perché calcola la giusta precisione per me. Ed è anche veloce .

#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;

Produzione:

Pi: 3.14159265358979


La documentazione di boost dice "Per i numeri che hanno una corrispondente specializzazione di std :: numeric_limits, la versione attuale ora sceglie una precisione da abbinare". Questo sembra il modo più semplice per ottenere la massima precisione. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo

11

Con la massima precisione, suppongo che la precisione sia sufficiente per mostrare la migliore approssimazione al valore desiderato, ma va sottolineato che doubleè memorizzato usando la rappresentazione di base 2 e la base 2 non può rappresentare qualcosa di così banale come 1.1esattamente. L'unico modo per ottenere la piena precisione del doppio reale (senza ERRORE ROTONDO) è stampare i bit binari (o i hex nybbles). Un modo per farlo è scrivere doublesu a unione quindi stampare il valore intero dei bit.

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

Questo ti darà la precisione accurata del 100% del doppio ... e sarà assolutamente illeggibile perché gli umani non possono leggere il doppio formato IEEE! Wikipedia ha una buona stesura su come interpretare i bit binari.

Nel C ++ più recente, puoi farlo

std::cout << std::hexfloat << 1.1;

10

Ecco come visualizzare un doppio con la massima precisione:

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

Questo mostra:

100,0000000000005


max_digits10 è il numero di cifre necessarie per rappresentare in modo univoco tutti i doppi valori distinti. max_digits10 rappresenta il numero di cifre prima e dopo il punto decimale.


Non usare set_precision (max_digits10) con std :: fixed.
In notazione fissa, set_precision () imposta il numero di cifre solo dopo il punto decimale. Ciò non è corretto poiché max_digits10 rappresenta il numero di cifre prima e dopo il punto decimale.

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

Questo visualizza un risultato errato:

100,00000000000049738

Nota: sono richiesti file di intestazione

#include <iomanip>
#include <limits>

4
Questo accade perché 100.0000000000005non viene rappresentato esattamente come double. (Potrebbe sembrare che dovrebbe, ma non lo è, perché viene normalizzato , cioè la sua rappresentazione binaria). Per vedere questo, provare: 100.0000000000005 - 100. Abbiamo capito 4.973799150320701e-13.
Evgeni Sergeev,

9

Come posso stampare un doublevalore con la massima precisione usando cout?

Usa hexfloato
usa scientifice imposta la precisione

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

Troppe risposte riguardano solo una delle 1) basi 2) layout fisso / scientifico o 3) precisione. Troppe risposte con precisione non forniscono il valore adeguato necessario. Da qui questa risposta a una vecchia domanda.

  1. Quale base?

A doubleè sicuramente codificato usando la base 2. Un approccio diretto con C ++ 11 è di stampare usando std::hexfloat.
Se un output non decimale è accettabile, abbiamo finito.

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. Altrimenti: fixedo scientific?

A doubleè un tipo a virgola mobile , non un punto fisso .

Do Non utilizzare std::fixedcome fallisce stampare piccole doublecome qualche cosa ma 0.000...000. In generale double, stampa molte cifre, forse centinaia di informazioni discutibili.

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 

Per stampare con la massima precisione, utilizzare innanzitutto std::scientific"scrivere i valori in virgola mobile nella notazione scientifica". Si noti che il valore predefinito di 6 cifre dopo il punto decimale, un importo insufficiente, viene gestito nel punto successivo.

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. Quanta precisione (quante cifre totali)?

UN double codifica che utilizza la base binaria 2 codifica la stessa precisione tra varie potenze di 2. Questo è spesso 53 bit.

[1.0 ... 2.0) ci sono 2 53 diversi double,
[2.0 ... 4.0) ci sono 2 53 diversi double,
[4.0 ... 8.0) ci sono 2 53 diversi double,
[8.0 ... 10.0) ci sono 2 / 8 * 2 53 diversidouble .

Tuttavia, se stampa di codici a decimale con Ncifre significative, il numero di combinazioni [1,0 ... 10,0) è 9/10 * 10 N .

Qualunque sia la scelta N(precisione), non ci sarà un mapping uno a uno tra doublee il testo decimale. Se Nviene scelto un valore fisso , a volte sarà leggermente più o meno del necessario per determinati doublevalori. Potremmo sbagliare su troppo pochi ( a)sotto) o troppi ( b)sotto).

3 candidati N:

a) Usare un Nmodo così quando si converte da testo double- testo arriviamo allo stesso testo per tutti double.

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

b) Usa un Ncosì quando convertiamo da double-testo- doublearriviamo allo stesso doubleper tutti double.

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

Quando max_digits10non è disponibile, tieni presente che a causa degli attributi base 2 e base 10 digits10 + 2 <= max_digits10 <= digits10 + 3, possiamo usaredigits10 + 3 per assicurare che vengano stampate cifre decimali sufficienti.

c) Utilizzare un valore Nche varia con il valore.

Ciò può essere utile quando il codice desidera visualizzare un testo minimo ( N == 1) o il valore esatto di a double( N == 1000-ishnel caso di denorm_min). Tuttavia, poiché si tratta di "lavoro" e probabilmente non dell'obiettivo dell'OP, verrà accantonato.


Di solito è b) che viene utilizzato per "stampare un doublevalore con la massima precisione". Alcune applicazioni potrebbero preferire a) errori nel non fornire troppe informazioni.

Con .scientific, .precision()imposta il numero di cifre da stampare dopo il punto decimale, quindi le 1 + .precision()cifre vengono stampate. Il codice ha bisogno di max_digits10cifre totali, quindi .precision()viene chiamato con a 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

Domanda C simile


Bella risposta! Alcune osservazioni però: hai ragione che precision()imposta il numero di cifre decimali per la modalità scientifica. Senza specificare scientific, imposta il numero totale di cifre, escluso l'esponente. Potresti comunque finire con un risultato scientifico, a seconda del valore numerico, ma potresti anche ottenere meno cifre di quanto specificato. Esempio: i cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"risultati per printfpotrebbero essere diversi. Cose confuse che uno dovrebbe essere consapevole.
Simpleton il

Per i posteri, ecco la lunghezza del buffer richiesta per la rappresentazione esatta esatta della stringa di tutti i doppi numeri in modalità scientifica usando printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);I caratteri aggiuntivi sono per: segno, punto decimale, zero finale, e [+ | -], 3 cifre per l'esponente ( DBL_MAX_10_EXP = 308). Quindi il numero totale di caratteri richiesti è 25.
Simpleton

Non posso modificare il mio primo commento, quindi eccoci di nuovo qui: un altro problema con la modalità scientifica è che potrebbe decidere di non utilizzare l'output esponenziale, potrebbe persino decidere di non utilizzare affatto l'output in virgola mobile. Vale a dire, produrrà 1.0 come "1", che potrebbe essere un problema in un contesto di serializzazione / deserializzazione. Puoi forzarlo a generare un punto decimale usando "% #. * G", ma questo ha lo svantaggio di aggiungere un numero di zeri finali, che non ha senza # ...
Simpleton

3
printf("%.12f", M_PI);

% .12f significa virgola mobile, con precisione di 12 cifre.


11
Questo non "usa cout".
Johnsyweb,

2
12 cifre non sono "piena precisione"
Roland Illig

0

Più facilmente ...

#include <limits>

using std::numeric_limits;

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

16
Sono curioso: perché il "+1"?
Éric Malenfant,

0

Con ostream :: precision (int)

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

cederà

3.141592653589793, 2.718281828459045

Perché devi dire "+1" Non ne ho idea, ma la cifra in più che ne ottieni è corretta.


3
numeric_limits <carattere senza segno> :: cifre 10 è uguale a 2. Perché può contenere qualsiasi numero decimale di due cifre 0..99. Può anche contenere 255 .. ma non 256, 257 ... 300 ecc. Ecco perché i numeri 10 non sono 3! Penso che "+1" sia stato aggiunto per superare qualcosa del genere.
Dmitriy Yurchenko,

0

Questo mostrerà il valore fino a due cifre decimali dopo il punto.

#include <iostream>
#include <iomanip>

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

Vedi qui: Notazione a virgola fissa

std :: fisso

Usa notazione a virgola mobile fissa Imposta il flag del formato floatfield per il flusso str su fisso.

Quando floatfield è impostato su fisso, i valori in virgola mobile vengono scritti usando la notazione in virgola fissa: il valore viene rappresentato con esattamente quante cifre nella parte decimale come specificato dal campo di precisione (precisione) e senza parte esponente.

std :: setprecision

Imposta precisione decimale Imposta la precisione decimale da utilizzare per formattare i valori in virgola mobile sulle operazioni di output.

Se hai familiarità con lo standard IEEE per rappresentare i virgola mobile, sapresti che è impossibile mostrare i virgola mobile con la massima precisione fuori dallo scopo dello standard , vale a dire, si tradurrà sempre in un arrotondamento del valore reale.

Devi prima verificare se il valore rientra nell'ambito , se sì, quindi utilizzare:

cout << defaultfloat << d ;

std :: defaultfloat

Usa notazione a virgola mobile predefinita Imposta il flag del formato floatfield per lo stream str su defaultfloat.

Quando floatfield è impostato su defaultfloat, i valori in virgola mobile vengono scritti utilizzando la notazione predefinita: la rappresentazione utilizza tutte le cifre significative necessarie fino alla precisione decimale del flusso (precisione), contando entrambe le cifre prima e dopo il punto decimale (se presente ).

Questo è anche il comportamento predefinito di cout, il che significa che non lo usi esplicitamente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.