2010-11-18 9 views
23

Necesito ayuda para mantener la precisión de double. Si asigno un literal a un doble, el valor real se truncó.¿Cómo 'cout' el número correcto de lugares decimales de un valor doble?

int main() { 
    double x = 7.40200133400; 
    std::cout << x << "\n"; 
} 

Para el fragmento de código anterior, la salida fue 7.402
¿Hay una manera de prevenir este tipo de truncamiento? ¿O hay una forma de calcular exactamente cuántos puntos flotantes para un double? Por ejemplo, number_of_decimal(x) daría 11, ya que la entrada es desconocida en tiempo de ejecución, así que no puedo usar setprecision().


creo que debería cambiar mi pregunta: Cómo convertir un doble con una cadena sin truncar los puntos flotantes. es decir

#include <iostream> 
#include <string> 
#include <sstream> 

template<typename T> 
std::string type_to_string(T data) { 
    std::ostringstream o; 
    o << data; 
    return o.str(); 
} 

int main() { 
    double x = 7.40200; 
    std::cout << type_to_string(x) << "\n"; 
} 

La salida esperada debe ser 7.40200 pero el resultado real fue 7.402. Entonces, ¿cómo puedo solucionar este problema? ¿Alguna idea?

+1

"El resultado esperado": el problema está con sus expectativas. "7.40200 pero el resultado real fue 7.402" - esos son iguales, por lo que nada fue "truncado". cout no puede saber cuántos ceros escribió en su archivo fuente. –

+0

Consulte [this] (http://www.cplusplus.com/reference/iostream/ios_base/precision/) para precisión de coma flotante. – Benoit

Respuesta

27

Debido al hecho de la float y double se almacenan internamente en el sistema binario, lo literal 7.40200133400 destaca realidad para el número 7,40200133400000037653398976544849574565887451171875

... así que ¿cómo mucha precisión lo que realmente quieres? :-)

#include <iomanip>  
int main() 
{ 
    double x = 7.40200133400; 
    std::cout << std::setprecision(51) << x << "\n"; 
} 

Y sí, este programa realmente imprime 7,40200133400000037653398976544849574565887451171875!

+0

Hola Fred, No quiero ninguna precisión especificada. Solo quiero convertirlo a una cadena como su formato original. Corrígeme si malinterpreté tu instrucción. Gracias, – Chan

+0

@ Chan: ¿Quieres '7.402001334' o' 7.40200133400' o '7.402001334000000'? – fredoverflow

+0

Quiero 7.40200133400, ni más ni menos. – Chan

9
std::cout << std::setprecision(8) << x; 

Tenga en cuenta que setprecision es persistente y todos los siguientes flotadores que impresión se imprimirá con esa precisión, hasta que se cambie a un valor diferente. Si eso es un problema y que desea evitar eso, se puede utilizar un proxy stringstream objeto:

std::stringstream s; 
s << std::setprecision(8) << x; 
std::cout << s.str(); 

Para obtener más información sobre el formato iostream, echa un vistazo a la sección Input/output manipulators en cppreference.

+0

Hola Kos, entiendo lo que dijiste, pero el problema fue que la precisión está cambiando de un valor a otro. Ejemplo: 1.233 y 3.99945, uno tiene 3 y uno tiene 5. – Chan

+0

-1, no es lo que pregunta el OP ... :( – Nim

+3

@Chan: todos los números de coma flotante tienen la misma precisión. – You

5

solución utilizando Boost.Format:

#include <boost/format.hpp> 
#include <iostream> 

int main() { 
    double x = 7.40200133400; 
    std::cout << boost::format("%1$.16f") % x << "\n"; 
} 

Esto da salida a 7.4020013340000004.

Espero que esto ayude!

+0

Primero, gracias Daniel. Pero creo que el formato boost :: se comporta exactamente como cout.set (0, std :: ios :: floatfield), y su resultado no es el original. Mi problema es que quiero convierta ese valor doble en una cadena. – Chan

+0

La original es solo algo que ha escrito en un archivo de texto. No representa necesariamente el valor real. –

3

¡La única respuesta a esto que he encontrado es que no hay manera de hacer esto (como en el cálculo de los lugares decimales) correctamente! La razón principal de esto es que la representación de un número puede no ser lo que esperas, por ejemplo, 128.82, parece lo suficientemente inocua, sin embargo, su representación real es 128.8199999999 ... ¿cómo se calcula el número de decimales allí?

+1

Puede agregar o eliminar 0.0000000001, luego imprimirlo de nuevo y ver si la nueva salida utiliza menos caracteres. La cantidad que se agrega o elimina depende de la precisión con la que esté satisfecho. –

+1

A menos que esté realmente preocupado por ese nivel de precisión, en cuyo caso es probable que estés haciendo lo tuyo, solo redondeas a 15 lugares. Después de 15 no tienes muy buena precisión de todos modos. –

+0

De hecho, quiero convertir este valor en una cadena, y quiero mantener su formato tal como se leyó desde un archivo. – Chan

17

Debe usar setiosflags(ios::fixed) y setprecision(x).

Por ejemplo, cout << setiosflags(ios::fixed) << setprecision(4) << myNumber << endl;

Además, no se olvide de #include <iomanip.h>.

+2

+1 para ios :: corregido como: setiosflags (ios :: fixed) ayuda para el caso cuando num == (int) num, permite dar salida a un número en formato como 2.00, sin que su salida sea 2 –

2

Respondiendo a la edición de respuestas: No hay forma de hacerlo. Tan pronto como asigne un valor a double, se perderán los ceros finales (para el compilador/computadora, 0.402, 0.4020 y 0.40200 son el MISMO NÚMERO). La única manera de retener los ceros finales como lo indicó es almacenar los valores como cadenas (o hacer trucos donde se hace un seguimiento de la cantidad de dígitos que le interesan y se formatea exactamente a esa longitud).

1

Hagamos una solicitud análoga: después de inicializar un número entero con 001, querrá imprimirlo con los ceros a la izquierda. Esa información de formato simplemente nunca se almacenó.

Para comprender mejor el almacenamiento de coma flotante de doble precisión, consulte el estándar IEEE 754.

2

Dobles no tienen posiciones decimales. Tienen lugares binarios. Y los lugares binarios y decimales son inconmensurables (porque log2(10) no es un número entero).

Lo que estás pidiendo no existe.

Cuestiones relacionadas