txt por manejo linea libreria lenguaje leer lectura escritura ejercicios crear como binarios archivos archivo c++ string file-io character-encoding file-format

c++ - por - La mejor manera de generar un doble de precisión completa en un archivo de texto.



libreria fstream c++ (9)

Necesito usar un archivo de texto existente para almacenar algunos valores muy precisos. Cuando se vuelve a leer, los números esencialmente deben ser exactamente equivalentes a los que se escribieron originalmente. Ahora, una persona normal usaría un archivo binario ... por varias razones, eso no es posible en este caso.

Entonces ... ¿alguno de ustedes tiene una buena manera de codificar un doble como una cadena de caracteres (aparte de aumentar la precisión)? Mi primer pensamiento fue lanzar el doble a un personaje [] y escribir los caracteres. No creo que vaya a funcionar porque algunos de los personajes no son visibles, producen sonidos e incluso terminan cadenas (''/ 0'' ... ¡Te estoy hablando!)

¿Pensamientos?

[Editar]: una vez que descubra cuál de las soluciones propuestas funciona mejor para mí, marcaré una como ''la'' solución.


Estaba seguro de que había un especificador de formato especial para printf (quizás %a ?) Que permitía imprimir la representación binaria de un flotador, pero no puedo encontrarlo.
Sin embargo, puedes probar esto:

int main(int argc, char* argv[]){ union fi { unsigned int i; float f; } num; num.f = 1.23f; printf("%X/n", num.i); return 0; }


No dices por qué el binario está fuera de los límites. Para su aplicación, ¿sería viable convertir el binario a una cadena ASCII hexadecimal?


Para imprimir largas listas de números en C ++ sin pérdida (escriba y lea en la misma arquitectura) lo uso (para el double s):

#include<iostream> #include<iomanip> #include<limits> #include<cmath> #include<sstream> int main(){ std::ostringstream oss; int prec = std::numeric_limits<double>::digits10+2; // generally 17 int exponent_digits = std::log10(std::numeric_limits<double>::max_exponent10)+1; // generally 3 int exponent_sign = 1; // 1.e-123 int exponent_symbol = 1; // ''e'' ''E'' int digits_sign = 1; int digits_dot = 1; // 1.2 int division_extra_space = 1; int width = prec + exponent_digits + digits_sign + exponent_sign + digits_dot + exponent_symbol + division_extra_space; double original = -0.000013213213e-100/33215.; oss << std::setprecision(prec) << std::setw(width) << original << std::setw(width) << original << std::setw(width) << original << ''/n''; oss << std::setprecision(prec) << std::setw(width) << 1. << std::setw(width) << 2. << std::setw(width) << -3. << ''/n''; }

huellas dactilares

-3.9780861056751466e-110 -3.9780861056751466e-110 -3.9780861056751466e-110 1 2 -3

En resumen, en mi caso es como configurar:

oss << std::precision(17) << std::setw(25) << original << ...;

En cualquier caso puedo probar si esto funciona, haciendo:

std::istringstream iss(oss.str()); double test; iss >> test; assert(test == original);


Podría usar la base 64. Esto le permitiría almacenar los valores exactos de los bytes en un archivo de texto.

No lo he usado, pero encontré esta library codificación / decodificación de base 64 para C ++.


Prueba esto:

double d = 0.2512958125912; std::ostringstream s; s << d;

Luego escribe s para archivar.


Representación de almacenamiento a un lado, ¿qué pasa con algo como esto. Sin embargo, valores especiales como -0, infinitos, NaN, etc. requerirían un manejo especial. También me "olvidé" de implementar exponentes negativos.

#include <stdio.h> #include <math.h> const int SCALE = 1<<(52/2); void put( double a ) { FILE* f = fopen( "dump.txt", "wb" ); int sign = (a<0); if( sign ) a=-a; int exp2 = 0; while( a>1 ) a/=2, exp2++; a*=SCALE; int m1 = floor(a); a = (a-m1)*SCALE; int m2 = floor(a); fprintf(f, "%i %i %i %i/n", sign, exp2, m1, m2 ); fclose(f); } double get( void ) { FILE* f = fopen( "dump.txt", "rb" ); double a; int sign, exp2, m1, m2; fscanf( f, "%i %i %i %i/n", &sign, &exp2, &m1, &m2 ); fclose(f); printf( "%i %i %i %i/n", sign, exp2, m1, m2 ); a = m2; a /= SCALE; a+= m1; a /= SCALE; while( exp2>0 ) a*=2, exp2--; if( a<0 ) a=-a; return a; } int main( void ) { union { double a; unsigned b[2]; }; a = 3.1415926; printf( "%.20lf %08X %08X/n", a, b[0], b[1] ); put( a ); a = get(); printf( "%.20lf %08X %08X/n", a, b[0], b[1] ); }


Si desea mantener el formato estrictamente humano, puede escribir el doble así:

#include <iomanip> #include <sstream> std::string doubleToText(const double & d) { std::stringstream ss; //ss << std::setprecision( std::numeric_limits<double>::digits10+2); ss << std::setprecision( std::numeric_limits<int>::max() ); ss << d; return ss.str(); }

std::numeric_limits<int>::max() mostrará con la máxima precisión decimal posible. Esto preservará el valor de manera más precisa en las implementaciones de punto flotante diferente Cambiar esa línea por la línea comentada usando std::numeric_limits<double>::digits10+2 dará la precisión suficiente para hacer que el doble sea recuperable en la plataforma para la cual se compila el código. Esto da un resultado mucho más corto y conserva tanta información como el doble puede representar de forma única.

Los operadores de flujo de C ++ no conservan los números desnormalizados o los infinitos y los no-números cuando leen cadenas. Sin embargo, la función strtod POSIX sí lo hace , y está definida por el estándar. Por lo tanto, la forma más precisa de leer un número decimal con una llamada de biblioteca estándar sería esta función:

#include <stdlib.h> double textToDouble(const std::string & str) { return strtod( str.c_str(), NULL ); }


Suponiendo que IEEE 754 doble, printf("%.17g/n", x) le dará suficientes dígitos para recrear el valor original.


Un proceso de dos pasos: primero use la flotación binaria / doble serialización y luego aplique la codificación de base 64 . El resultado no es legible por humanos, pero no perderá precisión.

Edición: (Gracias a fuzzyTew y dan04)

Probablemente sea posible una representación legible sin decimales y humana, pero requeriría mucho más espacio.