我需要使用现有的文本文件来存储一些非常精确的值。读回时,数字基本上必须与最初写入的数字完全相同。现在,正常人会使用二进制文件......出于多种原因,在这种情况下这是不可能的。
所以......你们中的任何人都有一种很好的方法可以将double编码为一串字符(除了提高精度)。我的第一个想法是将双重转换为char []并写出字符。我不认为这会起作用,因为有些角色不可见,产生声音,甚至终止字符串('\ 0'......我正在和你说话!)
思想?
[编辑] - 一旦我弄清楚哪个解决方案最适合我,我会将其标记为''解决方案。
答案 0 :(得分:16)
如果你想保持格式严格的人类可读性,你可以写出双重的:
#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()
将以最大可能的小数精度输出。这将在不同的浮点实现中最精确地保留值。使用std::numeric_limits<double>::digits10+2
交换注释行的那一行将提供足够的精度,使得在编译代码的平台上可以精确地恢复double。这样可以提供更短的输出并保留尽可能多的信息,就像double可以唯一表示的那样。
C ++流运算符在读取字符串时不保留非规范化数字或无穷大而不是数字。但是,POSIX strtod
函数执行,并由标准。因此,使用标准库调用读回十进制数的最精确方法是此函数:
#include <stdlib.h>
double textToDouble(const std::string & str)
{
return strtod( str.c_str(), NULL );
}
答案 1 :(得分:9)
假设IEEE 754加倍,printf("%.17g\n", x)
将为您提供足够的数字来重新创建原始值。
答案 2 :(得分:4)
两步流程:首先使用binary float/double serialization,然后应用base 64 encoding。结果不是人类可读的,但不会失去精确度。
编辑:(感谢fuzzyTew和dan04)
可能无损十进制和人类可读表示,但需要更多空间。
答案 3 :(得分:2)
您可以使用base 64.这将允许您将确切的字节值存储在文本文件中。
我没有使用它,但我发现这个基本64位编码/解码library用于C ++。
答案 4 :(得分:1)
我确信printf
(也许是%a
?)有一个特殊的格式说明符,允许打印浮点数的二进制表示,但我找不到它。
但是,你可以试试这个:
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;
}
答案 5 :(得分:1)
用C ++打印很长的数字列表而不会丢失(在相同的arquitecture中写入和读取)我使用它(对于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';
}
打印
-3.9780861056751466e-110 -3.9780861056751466e-110 -3.9780861056751466e-110
1 2 -3
总之,在我的情况下,它就像设置:
oss << std::precision(17) << std::setw(25) << original << ...;
在任何情况下,我都可以通过以下方式测试是否有效:
std::istringstream iss(oss.str());
double test; iss >> test;
assert(test == original);
答案 6 :(得分:0)
试试这个:
double d = 0.2512958125912;
std::ostringstream s;
s << d;
然后将s写入文件。
答案 7 :(得分:0)
你没有说为什么二进制是禁止的。对于您的应用程序,将二进制文件转换为十六进制ASCII字符串是否可行?
答案 8 :(得分:0)
除了存储表示,这样的事情怎么样。 像-0,无穷大,NaN等特殊值需要特殊 虽然处理。我也“忘记”实施负面指数。
#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] );
}