¿Cómo ‘cout’ el número correcto de lugares decimales de un valor doble?

Necesito ayuda para mantener la precisión de un double . Si asigno un literal a un doble, el valor real se truncó.

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

Para el fragmento de código anterior, la salida fue 7.402
¿Hay alguna manera de prevenir este tipo de truncamiento? ¿O hay una manera 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 a: Cómo convertir un doble en una cadena sin truncar los puntos flotantes. es decir

 #include  #include  #include  template 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"; } 

El resultado esperado debería ser 7.40200 pero el resultado real fue 7.402. Entonces, ¿cómo puedo solucionar este problema? ¿Alguna idea?

Debido al hecho de que el float y el double están almacenados internamente en binario, el literal 7.40200133400 realidad representa el número 7.40200133400000037653398976544849574565887451171875

… ¿Cuánta precisión realmente quieres? 🙂

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

Y sí, este progtwig realmente imprime 7.40200133400000037653398976544849574565887451171875!

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

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

Además, no te olvides de #include .

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

Tenga en cuenta que setprecision es persistente y todos los flotantes siguientes que imprima se imprimirán con esa precisión, hasta que lo cambie a un valor diferente. Si eso es un problema y desea solucionarlo, puede usar un objeto proxy de stringstream :

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

Para obtener más información sobre el formato iostream, consulte la sección de manipuladores de entrada / salida en cppreference.

Solución usando Boost.Format :

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

Esto produce 7.4020013340000004 .

¡Espero que esto ayude!

La única respuesta a esto que he encontrado es que no hay forma de hacerlo (¡como en el cálculo de los 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í?

Respondiendo a su edición de respuestas: no hay forma de hacer eso. Tan pronto como se asigna un valor a un double , se pierden los ceros finales (para el comstackdor / 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).

Los dobles no tienen lugares 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.

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 punto flotante de doble precisión, consulte el estándar IEEE 754.

La segunda parte de la pregunta, acerca de cómo conservar los ceros finales en un valor de coma flotante desde la especificación de valor hasta el resultado de salida, no tiene solución. Un valor de punto flotante no retiene la especificación de valor original. Parece que esta parte sin sentido fue agregada por un moderador SO.

Con respecto a la primera y original parte de la pregunta, que interpreto como cómo presentar todos los dígitos significativos de 7.40200133400 , es decir, con un resultado como 7.402001334 , puedes simplemente eliminar los ceros finales de un resultado de salida que incluye solo dígitos confiables en el double valor:

 #include  // assert #include  // std::(numeric_limits) #include  // std::(string) #include  // std::(ostringstream) namespace my{ // Visual C++2017 doesn't support comma-separated list for `using`: using std::fixed; using std::numeric_limits; using std::string; using std::ostringstream; auto max_fractional_digits_for_positive( double value ) -> int { int result = numeric_limits::digits10 - 1; while( value < 1 ) { ++result; value *= 10; } return result; } auto string_from_positive( double const value ) -> string { ostringstream stream; stream << fixed; stream.precision( max_fractional_digits_for_positive( value ) ); stream << value; string result = stream.str(); while( result.back() == '0' ) { result.resize( result.size() - 1 ); } return result; } auto string_from( double const value ) -> string { return (0?"" : value == 0? "0" : value < 0? "-" + string_from_positive( -value ) : string_from_positive( value ) ); } } #include auto main() -> int { using std::cout; cout << my::string_from( 7.40200133400 ) << "\n"; cout << my::string_from( 0.00000000000740200133400 ) << "\n"; cout << my::string_from( 128.82 ) << "\n"; } 

Salida:

 7.402001334
 0.000000000007402001334
 128.81999999999999

Puede considerar agregar lógica para el redondeo para evitar largas secuencias de 9, como en el último resultado.