Impressão dupla sem perder precisão

Como você imprime um dobro em um fluxo para que, quando lido, não perca a precisão?

Eu tentei:

std::stringstream ss;

double v = 0.1 * 0.1;
ss << std::setprecision(std::numeric_limits<T>::digits10) << v << " ";

double u;
ss >> u;
std::cout << "precision " << ((u == v) ? "retained" : "lost") << std::endl;

Isso não funcionou como eu esperava.

Mas posso aumentar a precisão (o que me surpreendeu ao pensar que dígitos10 eram o máximo necessário).

ss << std::setprecision(std::numeric_limits<T>::digits10 + 2) << v << " ";
                                                 //    ^^^^^^ +2

Tem a ver com o número de dígitos significativos e os dois primeiros não contam (0,01).

Alguém já olhou para representar exatamente números de ponto flutuante? Qual é o encantamento mágico exato no fluxo que eu preciso fazer?

Após algumas experimentações:

O problema estava com a minha versão original. Havia dígitos não significativos na sequência após o ponto decimal que afetaram a precisão.

Portanto, para compensar isso, podemos usar notação científica para compensar:

ss << std::scientific
   << std::setprecision(std::numeric_limits<double>::digits10 + 1)
   << v;

Isso ainda não explica a necessidade do +1.

Além disso, se eu imprimir o número com mais precisão, obterá mais precisão!

std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits10 + 1) << v << "\n";
std::cout << std::scientific << std::setprecision(std::numeric_limits<double>::digits) << v << "\n";

Isso resulta em:

1.000000000000000e-02
1.0000000000000002e-02
1.00000000000000019428902930940239457413554200000000000e-02

Com base na resposta da @Stephen Canon abaixo:

Podemos imprimir exatamente usando o formatador printf (), "% a" ou "% A". Para conseguir isso em C ++, precisamos usar os manipuladores fixos e científicos (consulte n3225: 22.4.2.2.2p5 Tabela 88)

std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
std::cout << v;

Por enquanto eu defini:

template<typename T>
std::ostream& precise(std::ostream& stream)
{
    std::cout.flags(std::ios_base::fixed | std::ios_base::scientific);
    return stream;
}

std::ostream& preciselngd(std::ostream& stream){ return precise<long double>(stream);}
std::ostream& precisedbl(std::ostream& stream) { return precise<double>(stream);}
std::ostream& preciseflt(std::ostream& stream) { return precise<float>(stream);}

Próximo: Como lidamos com NaN / Inf?

questionAnswers(6)

yourAnswerToTheQuestion