Comment imprimer une valeur double en toute précision avec cout?

332

J'ai donc obtenu la réponse à ma dernière question (je ne sais pas pourquoi je n'y ai pas pensé). J'imprimais une doubleutilisation coutqui a été arrondie quand je ne m'y attendais pas. Comment puis-je faire coutimprimer une doubleutilisation en toute précision?

Jason Punyon
la source

Réponses:

391

Vous pouvez définir la précision directement std::coutet utiliser le std::fixedspécificateur de format.

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

Vous pouvez #include <limits>obtenir la précision maximale d'un flotteur ou d'un double.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Bill le lézard
la source
46
Pourquoi conseillez-vous explicitement d'utiliser fixed? Avec double h = 6.62606957e-34;, fixedme donne 0.000000000000000et scientificsort 6.626069570000000e-34.
Arthur
36
La précision doit être de 17 (ou std :: numeric_limits <double> :: digits10 + 2) car 2 chiffres supplémentaires sont nécessaires lors de la conversion de la décimale en représentation binaire pour garantir que la valeur est arrondie à la même valeur d'origine. Voici un article avec quelques détails: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher
8
Est-ce vraiment la bonne réponse? Lorsque j'utilise manuellement un nombre élevé, je peux imprimer jusqu'à 51 chiffres de e approximatif, mais avec cout.precision(numeric_limits<double>::digits10 + 2);seulement 16 ....
Assimilater
6
Pour ceux qui cherchent où il mentionne 17 chiffres dans le papier @MikeFisher cité, c'est sous le théorème 15.
Emile Cormier
15
@MikeFisher Vous avez raison, C ++ 11 introduitmax_digits10 pour désigner la même chose. Correction de la réponse pour refléter cela.
legends2k
70

Utilisation std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Paul Beckingham
la source
2
Existe-t-il une sorte de macro ou énumération MAX_PRECISION ou quelque chose que je peux transmettre à std :: setPrecision?
Jason Punyon
2
std :: setprecision (15) pour un double (ok ou 16), log_10 (2 ** 53) ~ = 15,9
user7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant
6
Devrait être std::setprecision (17)pour le double, voir les commentaires sur la réponse de @Bill The Lizard.
Alec Jacobson
9
pour que std :: setprecision fonctionne, #include <iomanip> doit être inclus.
user2262504
24

Voici ce que j'utiliserais:

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

Fondamentalement, le package de limites a des caractéristiques pour tous les types intégrés.
L'un des traits des nombres à virgule flottante (float / double / long double) est l'attribut digits10. Cela définit la précision (j'oublie la terminologie exacte) d'un nombre à virgule flottante en base 10.

Voir: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Pour plus de détails sur les autres attributs.

Martin York
la source
12
Cet en-tête est nécessaire pour utiliser std::setprecision(): #include <iomanip>
Martin Berger
il devrait être std::numeric_limits<double>au lieu denumberic_limits<double>
niklasfi
2
Pourquoi ajoutez-vous 1à std::numeric_limits<double>::digits10?
Alessandro Jacopson
5
@LokiAstari Vous pouvez utiliser des C + 11 à la max_digits10place. Regardez ça .
legends2k
1
@AlecJacobson Cela devrait plutôt l'être max_digits10, pas arbitraire digits10+2. Dans le cas contraire, dans le cas float, long double, boost::multiprecision::float128cela ne fonctionne pas, car il vous auriez besoin au +3lieu de +2.
Ruslan
14

La manière iostreams est un peu maladroite. Je préfère utiliser boost::lexical_castcar il calcule la bonne précision pour moi. Et c'est rapide aussi.

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

Production:

Pi: 3.14159265358979

Timothy003
la source
La documentation de boost dit "Pour les chiffres qui ont une spécialisation correspondante de std :: numeric_limits, la version actuelle choisit maintenant une précision à faire correspondre". Cela semble être le moyen le plus simple d'obtenir la précision maximale. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Par pleine précision, je suppose que suffisamment de précision pour montrer la meilleure approximation de la valeur prévue, mais il convient de souligner que doublestocké en utilisant la représentation de base 2 et que la base 2 ne peut pas représenter quelque chose d'aussi trivial 1.1qu'exactement. La seule façon d'obtenir la précision totale du double réel (sans ERREUR ARRÊT ROND) est d'imprimer les bits binaires (ou les octets hexadécimaux). Une façon de procéder consiste à écrire le doubledans un union, puis à imprimer la valeur entière des bits.

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

Cela vous donnera la précision à 100% du double ... et sera totalement illisible car les humains ne peuvent pas lire le double format IEEE! Wikipedia a une bonne écriture sur la façon d'interpréter les bits binaires.

Dans le C ++ plus récent, vous pouvez faire

std::cout << std::hexfloat << 1.1;
Mark Lakata
la source
10

Voici comment afficher un double en toute précision:

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

Cela affiche:

100.0000000000005


max_digits10 est le nombre de chiffres nécessaires pour représenter de manière unique toutes les valeurs doubles distinctes. max_digits10 représente le nombre de chiffres avant et après le point décimal.


N'utilisez pas set_precision (max_digits10) avec std :: fixed.
En notation fixe, set_precision () définit le nombre de chiffres uniquement après le point décimal. Ceci est incorrect car max_digits10 représente le nombre de chiffres avant et après le point décimal.

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

Cela affiche un résultat incorrect:

100.00000000000049738

Remarque: fichiers d'en-tête requis

#include <iomanip>
#include <limits>
Daniel Laügt
la source
4
Cela se produit car 100.0000000000005n'est pas représenté exactement comme un double. (Cela peut sembler être le cas, mais ce n'est pas le cas, car il est normalisé , c'est-à-dire sa représentation binaire). Pour voir cela, essayez: 100.0000000000005 - 100. Nous obtenons 4.973799150320701e-13.
Evgeni Sergeev
9

Comment imprimer une doublevaleur en toute précision avec cout?

Utiliser hexfloatou
utiliser scientificet définir la précision

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

Trop de réponses ne concernent qu'une des 1) base 2) disposition fixe / scientifique ou 3) précision. Trop de réponses avec précision ne fournissent pas la valeur appropriée requise. D'où cette réponse à une vieille question.

  1. Quelle base?

A doubleest certainement codé en utilisant la base 2. Une approche directe avec C ++ 11 consiste à imprimer en utilisant std::hexfloat.
Si une sortie non décimale est acceptable, nous avons terminé.

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. Sinon: fixedou scientific?

A doubleest un type à virgule flottante , pas un point fixe .

Ne l' utilisez passtd::fixed car cela ne s'imprime pas doublecomme autre chose 0.000...000. Pour les grands double, il imprime de nombreux chiffres, peut-être des centaines d'informations douteuses.

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 

Pour imprimer avec une précision totale, utilisez d'abord std::scientificqui "écrit des valeurs à virgule flottante en notation scientifique". Notez la valeur par défaut de 6 chiffres après la virgule décimale, une quantité insuffisante, est gérée au point suivant.

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. Quelle précision (combien de chiffres au total)?

Un doublecodé à l'aide de la base binaire 2 code la même précision entre différentes puissances de 2. Il s'agit souvent de 53 bits.

[1.0 ... 2.0) il y en a 2 53 différents double,
[2.0 ... 4.0) il y en a 2 53 différents double,
[4.0 ... 8.0) il y en a 2 53 différents double,
[8.0 ... 10.0) il y en a 2 / 8 * 2 53 différents double.

Pourtant , si code imprime en décimal avec Nchiffres significatifs, le nombre de combinaisons [1.0 ... 10.0) est 9/10 * 10 N .

Quelle que soit la N(précision) choisie, il n'y aura pas de correspondance un à un entre doublele texte décimal et le texte décimal. Si un fixe Nest choisi, il sera parfois légèrement plus ou moins que vraiment nécessaire pour certaines doublevaleurs. Nous pourrions commettre des erreurs sur trop peu ( a)ci-dessous) ou trop ( b)ci-dessous).

3 candidat N:

a) Utilisez un Ntel lors de la conversion de texte-texte, doublenous arrivons au même texte pour tous double.

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

b) Utilisez un Nso lors de la conversion de double-text- doublenous arrivons au même doublepour tous double.

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

Lorsque max_digits10n'est pas disponible, notez qu'en raison des attributs base 2 et base 10 digits10 + 2 <= max_digits10 <= digits10 + 3, nous pouvons utiliser digits10 + 3pour garantir que suffisamment de chiffres décimaux sont imprimés.

c) Utilisez un Nqui varie avec la valeur.

Cela peut être utile lorsque le code souhaite afficher un texte minimal ( N == 1) ou la valeur exacte de a double( N == 1000-ishdans le cas de denorm_min). Pourtant, comme il s'agit de «travail» et peu probable de l'objectif du PO, il sera mis de côté.


C'est généralement b) qui est utilisé pour "imprimer une doublevaleur avec une précision totale". Certaines applications peuvent préférer a) à l'erreur de ne pas fournir trop d'informations.

Avec .scientific, .precision()définit le nombre de chiffres à imprimer après la virgule décimale pour que les 1 + .precision()chiffres soient imprimés. Le code a besoin d'un max_digits10nombre total de chiffres et .precision()est donc appelé avec un 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

Question C similaire

chux - Réintégrer Monica
la source
Très bonne réponse! Quelques remarques cependant: vous avez raison qui precision()définit le nombre de décimales pour le mode scientifique. Sans spécifier scientific, il définit le nombre total de chiffres, à l'exclusion de l'exposant. Vous pouvez toujours vous retrouver avec une sortie scientifique, en fonction de la valeur de votre numéro, mais vous pouvez également obtenir moins de chiffres que ce que vous avez spécifié. Exemple: les cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"résultats pour printfpeuvent être différents. Il faut être conscient des choses confuses.
Simpleton
Pour la postérité, voici la longueur de tampon requise pour une représentation exacte garantie de tous les nombres doubles en mode scientifique à l'aide de printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);les caractères supplémentaires sont pour: signe, virgule décimale, zéro de fin, e [+ | -], 3 chiffres pour l'exposant ( DBL_MAX_10_EXP = 308). Le nombre total de caractères requis est donc de 25.
Simpleton
Je ne peux pas modifier mon premier commentaire, alors nous y revenons: un autre problème avec le mode scientifique est qu'il pourrait décider de ne pas utiliser de sortie exponentielle, il pourrait même décider de ne pas utiliser du tout de sortie en virgule flottante. Autrement dit, il affichera 1.0 en tant que "1", ce qui pourrait être un problème dans un contexte de sérialisation / désérialisation. Vous pouvez le forcer à afficher un point décimal en utilisant "% #. * G", mais cela a l'inconvénient qu'il ajoute un certain nombre de zéros de fin, ce qu'il ne fait pas sans le # ...
Simpleton
3
printf("%.12f", M_PI);

% .12f signifie virgule flottante, avec une précision de 12 chiffres.

Maister
la source
11
Ce n'est pas "utiliser cout".
Johnsyweb
2
12 chiffres n'est pas "pleine précision"
Roland Illig
0

Plus facilement ...

#include <limits>

using std::numeric_limits;

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

la source
16
Je suis curieux: pourquoi le "+1"?
Éric Malenfant
0

Avec ostream :: precision (int)

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

donnera

3.141592653589793, 2.718281828459045

Pourquoi vous devez dire "+1" Je n'ai aucune idée, mais le chiffre supplémentaire que vous en retirez est correct.

Jann
la source
3
numeric_limits <unsigned char> :: digits10 est égal à 2. Parce qu'il peut contenir n'importe quel nombre décimal de deux chiffres 0..99. Il peut également contenir 255 .. mais pas 256, 257 ... 300 etc. c'est pourquoi digits10 n'est pas 3! Je pense que "+1" est ajouté pour surmonter quelque chose comme ça.
Dmitriy Yurchenko du
0

Cela montrera la valeur jusqu'à deux décimales après le point.

#include <iostream>
#include <iomanip>

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

Voir ici: notation à virgule fixe

std :: fixed

Utiliser une notation à virgule flottante fixe Définit l'indicateur de format de champ flottant pour le flux str sur fixe.

Lorsque floatfield est défini sur fixe, les valeurs à virgule flottante sont écrites à l'aide de la notation à virgule fixe: la valeur est représentée avec exactement autant de chiffres dans la partie décimale que spécifié par le champ de précision (précision) et sans partie exposante.

std :: setprecision

Définir la précision décimale Définit la précision décimale à utiliser pour formater les valeurs à virgule flottante sur les opérations de sortie.

Si vous connaissez la norme IEEE pour représenter les virgules flottantes, vous savez qu'il est impossible d'afficher des virgules flottantes avec une précision totale hors du champ d'application de la norme , c'est-à-dire que cela se traduira toujours par un arrondi de la valeur réelle.

Vous devez d'abord vérifier si la valeur est dans la portée , si oui, utilisez ensuite:

cout << defaultfloat << d ;

std :: defaultfloat

Utiliser la notation à virgule flottante par défaut Définit l'indicateur de format de champ flottant pour le flux str sur flottant par défaut.

Lorsque floatfield est défini sur defaultfloat, les valeurs à virgule flottante sont écrites en utilisant la notation par défaut: la représentation utilise autant de chiffres significatifs que nécessaire jusqu'à la précision décimale (précision) du flux, en comptant les deux chiffres avant et après le point décimal (le cas échéant) ).

C'est également le comportement par défaut de cout, ce qui signifie que vous ne l'utilisez pas explicitement.

emmmphd
la source