¿Cómo imprimo un valor doble con total precisión usando cout?

Respuestas:

391

Puede establecer la precisión directamente std::couty utilizar el std::fixedespecificador de formato.

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

Puede #include <limits>obtener la máxima precisión de un flotador o doble.

#include <limits>

typedef std::numeric_limits< double > dbl;

double d = 3.14159265358979;
cout.precision(dbl::max_digits10);
cout << "Pi: " << d << endl;
Bill el lagarto
fuente
46
¿Por qué aconseja explícitamente usar fixed? Con double h = 6.62606957e-34;, fixedme da 0.000000000000000y scientificsalidas 6.626069570000000e-34.
Arthur
36
La precisión debe ser 17 (o std :: numeric_limits <double> :: digits10 + 2) porque se necesitan 2 dígitos adicionales al convertir de decimal a la representación binaria para garantizar que el valor se redondea al mismo valor original. Aquí hay un documento con algunos detalles: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Mike Fisher
8
¿Es realmente la respuesta correcta? Cuando uso manualmente un número alto, puedo imprimir hasta 51 dígitos de e aproximada, pero con cout.precision(numeric_limits<double>::digits10 + 2);solo obtengo 16 ...
Asimilater
66
Para aquellos que buscan donde se mencionan 17 dígitos en el papel que cita @MikeFisher, está bajo el Teorema 15.
Emile Cormier
15
@ MikeFisher Tienes razón, C ++ 11 introducemax_digits10 para denotar lo mismo. Se corrigió la respuesta para reflejar esto.
legends2k
70

Uso std::setprecision:

std::cout << std::setprecision (15) << 3.14159265358979 << std::endl;
Paul Beckingham
fuente
2
¿Hay algún tipo de macro MAX_PRECISION o enumeración o algo que pueda pasar a std :: setPrecision?
Jason Punyon
2
std :: setprecision (15) para un doble (ok o 16), log_10 (2 ** 53) ~ = 15.9
user7116
14
std :: setprecision (std :: numeric_limits <double> :: digits10)
Éric Malenfant
66
Debería ser std::setprecision (17)doble, ver los comentarios en la respuesta de @Bill The Lizard.
Alec Jacobson
99
para que std :: setprecision funcione, se debe incluir #include <iomanip>.
user2262504
24

Esto es lo que usaría:

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

Básicamente, el paquete de límites tiene rasgos para todos los tipos de compilación.
Uno de los rasgos para los números de coma flotante (flotante / doble / largo doble) es el atributo digits10. Esto define la precisión (se me olvida la terminología exacta) de un número de coma flotante en la base 10.

Consulte: http://www.cplusplus.com/reference/std/limits/numeric_limits.html
Para obtener detalles sobre otros atributos.

Martin York
fuente
12
Este encabezado es necesario para usar std::setprecision(): #include <iomanip>
Martin Berger
debería ser en std::numeric_limits<double>lugar denumberic_limits<double>
niklasfi
2
¿Por qué agregas 1a std::numeric_limits<double>::digits10?
Alessandro Jacopson
55
@LokiAstari Puedes usar C + 11 en su max_digits10lugar. Mira esto .
legends2k
1
@AlecJacobson Debería ser max_digits10, no algo arbitrario digits10+2. De lo contrario, en el caso de float, long double, boost::multiprecision::float128se producirá un error, ya que lo que se necesita +3en lugar de +2.
Ruslan
14

La forma de iostreams es un poco torpe. Prefiero usar boost::lexical_castporque calcula la precisión correcta para mí. Y también es rápido .

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

Salida:

Pi: 3.14159265358979

Timoteo003
fuente
La documentación de impulso dice "Para los números que tienen una especialización correspondiente de std :: numeric_limits, la versión actual ahora elige una precisión para que coincida". Esta parece ser la forma más fácil de obtener la máxima precisión. ( boost.org/doc/libs/1_58_0/doc/html/boost_lexical_cast/… )
JDiMatteo
11

Con total precisión, supongo que la precisión es lo suficientemente mala como para mostrar la mejor aproximación al valor deseado, pero debe señalarse que doublese almacena utilizando la representación de base 2 y la base 2 no puede representar algo tan trivial como 1.1exactamente. La única forma de obtener la precisión completa del doble real (sin ERROR DE REDONDEO) es imprimir los bits binarios (o nibbles hexadecimales). Una forma de hacerlo es escribir doubleen unionay luego imprimir el valor entero de los bits.

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

¡Esto le dará la precisión 100% precisa del doble ... y será completamente ilegible porque los humanos no pueden leer el formato doble IEEE! Wikipedia tiene una buena redacción sobre cómo interpretar los bits binarios.

En la versión más reciente de C ++, puedes hacer

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

Aquí se explica cómo mostrar un doble con total precisión:

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

Esto muestra:

100.0000000000005


max_digits10 es el número de dígitos que son necesarios para representar de forma exclusiva todos los valores dobles distintos. max_digits10 representa el número de dígitos antes y después del punto decimal.


No use set_precision (max_digits10) con std :: fixed.
En notación fija, set_precision () establece el número de dígitos solo después del punto decimal. Esto es incorrecto ya que max_digits10 representa el número de dígitos antes y después del punto decimal.

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

Esto muestra un resultado incorrecto:

100.00000000000049738

Nota: Se requieren archivos de encabezado

#include <iomanip>
#include <limits>
Daniel Laügt
fuente
44
Esto sucede porque 100.0000000000005no se representa exactamente como a double. (Puede parecer que debería, pero no lo hace, porque se normaliza , es decir, su representación binaria). Para ver esto, trate de: 100.0000000000005 - 100. Nosotros conseguimos 4.973799150320701e-13.
Evgeni Sergeev
9

¿Cómo imprimo un doublevalor con total precisión usando cout?

Usar hexfloato
usar scientificy establecer la precisión

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

Demasiadas respuestas abordan solo una de 1) base 2) diseño fijo / científico o 3) precisión. Demasiadas respuestas con precisión no proporcionan el valor adecuado necesario. De ahí esta respuesta a una vieja pregunta.

  1. Que base

C doubleestá ciertamente codificado usando la base 2. Un enfoque directo con C ++ 11 es imprimir usando std::hexfloat.
Si una salida no decimal es aceptable, hemos terminado.

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. De lo contrario: fixedo scientific?

A doublees un tipo de punto flotante , no un punto fijo .

No , no utilice std::fixedcomo que no se imprime pequeña doublecomo algo más 0.000...000. Para grandes double, imprime muchos dígitos, quizás cientos de informativos cuestionables.

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 con total precisión, primero use el std::scientificcual "escribirá valores de punto flotante en notación científica". Observe que el valor predeterminado de 6 dígitos después del punto decimal, una cantidad insuficiente, se maneja en el siguiente punto.

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. ¿Cuánta precisión (cuántos dígitos totales)?

Un doublecodificado utilizando la base binaria 2 codifica la misma precisión entre varias potencias de 2. Esto suele ser de 53 bits.

[1.0 ... 2.0) hay 2 53 diferentes double,
[2.0 ... 4.0) hay 2 53 diferentes double,
[4.0 ... 8.0) hay 2 53 diferentes double,
[8.0 ... 10.0) hay 2 / 8 * 2 53 diferentes double.

Sin embargo, si las impresiones de código en decimal con Ndígitos significativos, el número de combinaciones [1.0 ... 10.0) es 9/10 * 10 N .

Cualquiera que sea la N(precisión) elegida, no habrá un mapeo uno a uno entre doubley el texto decimal. Si Nse elige un fijo , a veces será un poco más o menos de lo realmente necesario para ciertos doublevalores. Podríamos cometer un error en muy pocos ( a)abajo) o demasiados ( b)abajo).

3 candidato N:

a) Use un Nso cuando convierta doubletexto- -texto llegamos al mismo texto para todos double.

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

b) Use un Nso cuando convierta de double-texto- doublellegamos a lo mismo doublepara todos double.

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

Cuando max_digits10no está disponible, tenga en cuenta que debido a los atributos de base 2 y base 10 digits10 + 2 <= max_digits10 <= digits10 + 3, podemos usar digits10 + 3para asegurar que se impriman suficientes dígitos decimales.

c) Use un Nque varía con el valor.

Esto puede ser útil cuando el código quiere mostrar texto mínimo ( N == 1) o el valor exacto de a double( N == 1000-ishen el caso de denorm_min). Sin embargo, dado que esto es "trabajo" y no es probable que sea el objetivo de OP, se dejará de lado.


Generalmente es b) que se usa para "imprimir un doublevalor con total precisión". Algunas aplicaciones pueden preferir a) al error al no proporcionar demasiada información.

Con .scientific, .precision()establece el número de dígitos para imprimir después del punto decimal, por lo que 1 + .precision()se imprimen los dígitos. El código necesita max_digits10dígitos totales, por lo que .precision()se llama 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

Pregunta C similar

chux - Restablece a Monica
fuente
¡Gran respuesta! Sin embargo, algunas observaciones: Tienes razón, eso precision()establece el número de lugares decimales para el modo científico. Sin especificar scientific, establece el número total de dígitos, excluyendo el exponente. Aún puede terminar con resultados científicos, dependiendo de su valor numérico, pero también puede obtener menos dígitos de los que especificó. Ejemplo: Los cout.precision(3); cout << 1.7976931348623158e+308; // "1.8e+308"resultados para printfpueden ser diferentes. Cosas confusas que uno debe tener en cuenta.
Simpleton
Para la posteridad, aquí está la longitud de búfer requerida para la representación de cadena exacta garantizada de todos los números dobles en modo científico usando printf: char buf[DBL_DECIMAL_DIG + 3 + 5]; sprintf(buf, "%.*g", DBL_DECIMAL_DIG, d);Los caracteres adicionales son para: signo, punto decimal, cero final, e [+ | -], 3 dígitos para el exponente ( DBL_MAX_10_EXP = 308). Por lo tanto, el número total de caracteres requeridos es 25.
Simpleton
No puedo editar mi primer comentario, así que aquí vamos de nuevo: otro problema con el modo científico es que podría decidir no usar la salida exponencial, incluso podría decidir no usar la salida de punto flotante en absoluto. Es decir, generará 1.0 como "1", lo que podría ser un problema en un contexto de serialización / deserialización. Puede forzarlo a generar un punto decimal utilizando "% #. * G", pero esto tiene el inconveniente de que agrega una cantidad de ceros al final, lo que no ocurre sin el # ...
Simpleton
3
printf("%.12f", M_PI);

% .12f significa punto flotante, con precisión de 12 dígitos.

Señor
fuente
11
Esto no es "usar cout".
Johnsyweb
2
12 dígitos no es "precisión total"
Roland Illig
0

Lo más portátil ...

#include <limits>

using std::numeric_limits;

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

fuente
16
Tengo curiosidad: ¿por qué el "+1"?
Éric Malenfant
0

Con ostream :: precision (int)

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

rendirá

3.141592653589793, 2.718281828459045

¿Por qué tienes que decir "+1"? No tengo idea, pero el dígito extra que obtienes es correcto.

Jann
fuente
3
numeric_limits <unsigned char> :: digits10 es igual a 2. Porque puede contener cualquier número decimal de dos dígitos 0..99. También puede contener 255 ... pero no 256, 257 ... 300 etc. ¡Por eso digits10 no es 3! Creo que se agrega "+1" para superar algo como esto.
Dmitriy Yurchenko
0

Esto mostrará el valor hasta dos decimales después del punto.

#include <iostream>
#include <iomanip>

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

Ver aquí: notación de punto fijo

std :: fijo

Usar notación de punto flotante fija Establece el indicador de formato de campo flotante para la secuencia str en fijo.

Cuando floatfield se establece en fijo, los valores de punto flotante se escriben usando notación de punto fijo: el valor se representa con exactamente tantos dígitos en la parte decimal como se especifica en el campo de precisión (precisión) y sin parte de exponente.

std :: setprecision

Establecer precisión decimal Establece la precisión decimal que se utilizará para formatear valores de punto flotante en las operaciones de salida.

Si está familiarizado con el estándar IEEE para representar los puntos flotantes, sabrá que es imposible mostrar los puntos flotantes con total precisión fuera del alcance del estándar , es decir, siempre resultará en Un redondeo del valor real.

Primero debe verificar si el valor está dentro del alcance , en caso afirmativo, luego use:

cout << defaultfloat << d ;

std :: defaultfloat

Usar notación de punto flotante predeterminada Establece el indicador de formato de campo flotante para la secuencia str en defaultfloat.

Cuando floatfield se establece en defaultfloat, los valores de punto flotante se escriben usando la notación predeterminada: la representación utiliza tantos dígitos significativos como sea necesario hasta la precisión decimal (precisión) de la secuencia, contando los dígitos antes y después del punto decimal (si corresponde) )

Ese es también el comportamiento predeterminado de cout, lo que significa que no lo usa explícitamente.

emmmphd
fuente