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


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;

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;

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.


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


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;

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>

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


Ó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


0

Mais portàvel ...

#include <limits>

using std::numeric_limits;

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

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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.