Como imprimo um valor duplo com precisão total usando cout?

332

Então, eu recebi a resposta para minha última pergunta (não sei por que não pensei nisso). Eu estava imprimindo um doubleuso coutque era arredondado quando não esperava. Como faço para coutimprimir doubleusando precisão total?

Jason Punyon
fonte

Respostas:

391

Você pode definir a precisão diretamente std::coute usar o std::fixedespecificador de formato.

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

Você pode #include <limits>obter a precisão máxima de um flutuador ou duplo.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Bill the Lizard
fonte
46
Por que você aconselha explicitamente o uso fixed? Com double h = 6.62606957e-34;, fixedme dá 0.000000000000000e scientificsaídas 6.626069570000000e-34.
Arthur
36
A precisão precisa ser 17 (ou std :: numeric_limits <double> :: digits10 + 2) porque são necessários 2 dígitos extras ao converter de decimal de volta para a representação binária para garantir que o valor seja arredondado para o mesmo valor original. Aqui está um documento com alguns detalhes: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher
8
É realmente a resposta certa? Quando eu uso manualmente um número alto, eu posso imprimir tantos como 51 dígitos do aproximada e, mas com cout.precision(numeric_limits<double>::digits10 + 2);só consigo 16 ....
Assimilater
6
Para aqueles que procuram onde ele menciona 17 dígitos no jornal @MikeFisher citado, é sob Teorema 15.
Emile Cormier
15
@ MikeFisher Você está certo, o C ++ 11 apresentamax_digits10 para denotar o mesmo. Corrigida a resposta para refletir isso.
precisa saber é o seguinte
70

Use std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Paul Beckingham
fonte
2
Existe algum tipo de macro ou enum MAX_PRECISION ou algo que eu possa passar para std :: setPrecision?
Jason Punyon
2
std :: setprecision (15) para um duplo (ok, ou 16), log_10 (2 ** 53) ~ = 15,9
user7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant
6
Deve ser std::setprecision (17)duplo, veja os comentários na resposta de @Bill The Lizard.
Alec Jacobson
9
para que std :: setprecision funcione, deve incluir #include <iomanip>.
user2262504
24

Aqui está o que eu usaria:

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

Basicamente, o pacote de limites possui características para toda a construção em tipos.
Uma das características dos números de ponto flutuante (float / double / long double) é o atributo digits10. Isso define a precisão (esqueço a terminologia exata) de um número de ponto flutuante na base 10.

Consulte: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Para obter detalhes sobre outros atributos.

Martin York
fonte
12
Este cabeçalho é necessário para usar std::setprecision(): #include <iomanip>
Martin Berger
deve ser em std::numeric_limits<double>vez denumberic_limits<double>
niklasfi
2
Por que você adiciona 1a std::numeric_limits<double>::digits10?
Alessandro Jacopson
5
@LokiAstari Você pode usar C + 11's max_digits10. Veja isso .
legends2k
1
@AlecJacobson Deveria ser max_digits10um pouco arbitrário digits10+2. Caso contrário, no caso de float, long double, boost::multiprecision::float128isso irá falhar, uma vez que você precisa +3, em vez de +2.
Ruslan
14

A maneira iostreams é meio desajeitada. Eu prefiro usar boost::lexical_castporque calcula a precisão certa para mim. E é rápido também.

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

Resultado:

Pi: 3.14159265358979

Timothy003
fonte
A documentação do impulso diz "Para números que possuem uma especialização correspondente de std :: numeric_limits, a versão atual agora escolhe uma precisão para corresponder". Esta parece ser a maneira mais fácil de obter a precisão máxima. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Por precisão total, suponho precisão média suficiente para mostrar a melhor aproximação ao valor pretendido, mas deve-se ressaltar que doubleé armazenado usando a representação da base 2 e a base 2 não pode representar algo tão trivial quanto 1.1exatamente. A única maneira de obter a precisão total do dobro real (com SEM ROUND OFF ERROR) é imprimir os bits binários (ou nybbles hexadecimais). Uma maneira de fazer isso é escrever doublepara unione depois imprimir o valor inteiro dos bits.

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

Isso lhe dará a precisão 100% exata do duplo ... e será totalmente ilegível porque os humanos não podem ler o formato duplo IEEE! A Wikipedia tem uma boa descrição de como interpretar os bits binários.

No C ++ mais recente, você pode fazer

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

Aqui está como exibir um duplo com precisão total:

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

Isso exibe:

100.0000000000005


max_digits10 é o número de dígitos necessários para representar exclusivamente todos os valores duplos distintos. max_digits10 representa o número de dígitos antes e depois do ponto decimal.


Não use set_precision (max_digits10) com std :: fixed.
Na notação fixa, set_precision () define o número de dígitos somente após o ponto decimal. Isso está incorreto, pois max_digits10 representa o número de dígitos antes e depois do ponto decimal.

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

Isso exibe resultado incorreto:

100.00000000000049738

Nota: Arquivos de cabeçalho necessários

#include <iomanip>
#include <limits>
Daniel Laügt
fonte
4
Isso acontece porque 100.0000000000005não está sendo representado exatamente como a double. (Pode parecer que deveria, mas não, porque é normalizado , ou seja, sua representação binária). Para ver isso, tente: 100.0000000000005 - 100. Nós recebemos 4.973799150320701e-13.
Evgeni Sergeev
9

Como imprimo um doublevalor com precisão total usando cout?

Use hexfloatou
use scientifice defina a precisão

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

Muitas respostas tratam apenas de 1) base 2) layout fixo / científico ou 3) precisão. Muitas respostas com precisão não fornecem o valor adequado necessário. Daí esta resposta a uma pergunta antiga.

  1. Qual base?

A doublecertamente é codificado usando a base 2. Uma abordagem direta com o C ++ 11 é imprimir usando std::hexfloat.
Se uma saída não decimal é aceitável, estamos prontos.

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. Caso contrário: fixedou scientific?

A doubleé um tipo de ponto flutuante , não ponto fixo .

Você não utilizar std::fixedcomo que não consegue imprimir pequena doublecomo qualquer coisa mas 0.000...000. Em geral double, imprime muitos dígitos, talvez centenas de informações questionáveis.

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 

Para imprimir com precisão total, use primeiro o std::scientificque "escreverá valores de ponto flutuante em notação científica". Observe que o padrão de 6 dígitos após o ponto decimal, uma quantidade insuficiente, é tratado no próximo ponto.

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 precisão (quantos dígitos totais)?

Um doublecodificado usando a base binária 2 codifica a mesma precisão entre várias potências de 2. Isso geralmente é 53 bits.

[1,0 ... 2,0) existem 2 53 diferentes double,
[2,0 ... 4,0) existem 2 53 diferentes double,
[4,0 ... 8,0) existem 2 53 diferentes double,
[8,0 ... 10,0) existem 2 / 8 * 2 53 diferentes double.

No entanto, se impressões de código decimal com Nalgarismos significativos, o número de combinações [1.0 ... 10.0) é 10/9 * 10 N .

Qualquer que seja N(precisão) escolhida, não haverá um mapeamento individual entre doublee texto decimal. Se um fixo Nfor escolhido, algumas vezes será um pouco mais ou menos do que o necessário para determinados doublevalores. Podemos errar em muito poucos ( a)abaixo) ou muitos ( b)abaixo).

3 candidato N:

a) Use um Nmodo quando, ao converter de texto em doubletexto, chegamos ao mesmo texto para todos double.

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

b) Use um Nso ao converter de double-text- doublechegamos ao mesmo doublepara todos double.

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

Quando max_digits10não estiver disponível, observe que, devido aos atributos da base 2 e da base 10 digits10 + 2 <= max_digits10 <= digits10 + 3, podemos usar digits10 + 3para garantir que dígitos decimais suficientes sejam impressos.

c) Use um Nque varia com o valor.

Isso pode ser útil quando o código deseja exibir o mínimo de texto ( N == 1) ou o valor exato de a double( N == 1000-ishno caso de denorm_min). No entanto, como esse é "trabalho" e provavelmente não é o objetivo do OP, será deixado de lado.


Geralmente é b) usado para "imprimir um doublevalor com precisão total". Alguns aplicativos podem preferir: a) erro ao não fornecer muitas informações.

Com .scientific, .precision()define o número de dígitos a serem impressos após o ponto decimal, para que os 1 + .precision()dígitos sejam impressos. O código precisa de max_digits10dígitos totais, então .precision()é chamado com 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

Pergunta C semelhante

chux - Restabelecer Monica
fonte
Ótima resposta! Porém, algumas observações: você está certo ao precision()definir o número de casas decimais para o modo científico. Sem especificar scientific, ele define o número total de dígitos, excluindo o expoente. Você ainda pode ter resultados científicos, dependendo do valor numérico, mas também poderá obter menos dígitos do que o especificado. Exemplo: os cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"resultados para printfpodem ser diferentes. Coisas confusas que devemos ter em mente.
Simpleton 4/03
Para a posteridade, eis o comprimento do buffer necessário para garantir a representação exata de seqüência de todos os números duplos no modo científico usando printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);Os caracteres extras são para: sinal, ponto decimal, zero à direita, e [+ | -], 3 dígitos para o expoente ( DBL_MAX_10_EXP = 308). Portanto, o número total de caracteres necessários é 25.
Simpleton
Não é possível editar meu primeiro comentário, então vamos lá novamente: Outro problema no modo científico é que ele pode decidir não usar saída exponencial, pode até decidir não usar saída de ponto flutuante. Ou seja, ele produzirá 1,0 como "1", o que pode ser um problema em um contexto de serialização / desserialização. Você pode forçar a saída de um ponto decimal, usando, mas isso tem a desvantagem de que ele adiciona um número de zeros à direita, o que não acontece sem o # ... "% # * g."
Simplório
3
printf("%.12f", M_PI);

% .12f significa ponto flutuante, com precisão de 12 dígitos.

Maister
fonte
11
Isso não é "usando cout".
precisa saber é o seguinte
2
12 dígitos não é "precisão total"
Roland Illig
0

Mais portàvel ...

#include <limits>

using std::numeric_limits;

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

fonte
16
Estou curioso: por que o "+1"?
Éric Malenfant
0

Com ostream :: precision (int)

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

vai render

3.141592653589793, 2.718281828459045

Por que você tem que dizer "+1" Eu não tenho idéia, mas o dígito extra que você obtém está correto.

Jann
fonte
3
numeric_limits <char não assinado> :: digits10 é igual a 2. Porque ele pode conter qualquer número decimal de dois dígitos 0..99. Também pode conter 255 .. mas não 256, 257 ... 300 etc. é por isso que digits10 não é 3! Eu acho que "+1" foi adicionado para superar algo assim.
Dmitriy Yurchenko
0

Isso mostrará o valor até duas casas decimais após o ponto.

#include <iostream>
#include <iomanip>

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

Veja aqui: Notação de ponto fixo

std :: fixed

Usar notação de ponto flutuante fixo Define o sinalizador de formato de campo flutuante para o fluxo str ser fixo.

Quando floatfield é definido como fixo, os valores de ponto flutuante são gravados usando a notação de ponto fixo: o valor é representado com exatamente o número de dígitos na parte decimal, conforme especificado pelo campo de precisão (precisão) e sem a parte do expoente.

std :: setprecision

Definir precisão decimal Define a precisão decimal a ser usada para formatar valores de ponto flutuante nas operações de saída.

Se você estiver familiarizado com o padrão IEEE para representar os pontos flutuantes, saberia que é impossível mostrar pontos flutuantes com precisão total fora do escopo do padrão , ou seja, isso sempre resultará em um arredondamento do valor real.

Você precisa primeiro verificar se o valor está dentro do escopo ; se sim, use:

cout << defaultfloat << d ;

std :: defaultfloat

Usar notação de ponto flutuante padrão Define o sinalizador de formato floatfield para o fluxo str como defaultfloat.

Quando floatfield é definido como defaultfloat, os valores de ponto flutuante são gravados usando a notação padrão: a representação usa quantos dígitos significativos forem necessários até a precisão decimal (precisão) do fluxo, contando os dígitos antes e depois do ponto decimal (se houver) )

Esse também é o comportamento padrão de cout, o que significa que você não o usa explicitamente.

emmmphd
fonte