将全精度双精度输出到文本文件的最佳方法

发布于 2024-10-10 20:28:53 字数 302 浏览 4 评论 0原文

我需要使用现有的文本文件来存储一些非常精确的值。当读回时,这些数字基本上需要与最初写入的数字完全相同。现在,普通人会使用二进制文件......出于多种原因,在这种情况下这是不可能的。

那么...你们中的任何人是否有一种将双精度数编码为字符串的好方法(除了提高精度之外)。我的第一个想法是将 double 转换为 char[] 并写出字符。我认为这行不通,因为有些字符不可见,会产生声音,甚至终止字符串('\0'...我在跟你说话!)

有想法吗?

[编辑] - 一旦我弄清楚所提出的哪种解决方案最适合我,我会将其中一个标记为“最佳”解决方案。

I need to use an existing text file to store some very precise values. When read back in, the numbers essentially need to be exactly equivalent to the ones that were originally written. Now, a normal person would use a binary file... for a number of reasons, that's not possible in this case.

So... do any of you have a good way of encoding a double as a string of characters (aside from increasing the precision). My first thought was to cast the double to a char[] and write out the chars. I don't think that's going to work because some of the characters are not visible, produce sounds, and even terminate strings ('\0'... I'm talkin to you!)

Thoughts?

[Edit] - once I figure out which of the solutions proposed works best for me, I'll mark one as 'the' solution.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

时光倒影 2024-10-17 20:28:53

如果您想保持格式严格为人类可读,您可以这样写出双精度数:

#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::max() 将以最大可能的十进制精度输出。这将在不同的浮点实现中最精确地保留该值。使用 std::numeric_limits::digits10+2 将该行替换为注释行将提供足够的精度,使 double 在代码编译的平​​台上可以精确恢复。这给出了更短的输出并保留了双精度可以唯一表示的尽可能多的信息。

C++ 流运算符在读取字符串时不会保留非规范化数字或无穷大和非数字。但是,POSIX strtod 函数,并且定义为标准。因此,通过标准库调用读回十进制数的最精确方法是以下函数:

#include <stdlib.h>

double textToDouble(const std::string & str)
{
    return strtod( str.c_str(), NULL );
}

If you want to keep the format strictly human readable, you can write out the double thusly:

#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() will output with the maximum possible decimal precision. This will preserve the value most precisely across differently floating point implementations. Swapping that line for the commented line using std::numeric_limits<double>::digits10+2 will give just enough precision to make the double precisely recoverable on the platform the code is compiled for. This gives a much shorter output and preserves as much information as the double can uniquely represent.

The C++ stream operators do not preserve denormalized numbers or the infinities and not-a-numbers when reading strings in. However, the POSIX strtod function does, and is defined to by the standard. Hence, the most precise way to read a decimal number back with a standard library call would be this function:

#include <stdlib.h>

double textToDouble(const std::string & str)
{
    return strtod( str.c_str(), NULL );
}
猫弦 2024-10-17 20:28:53

假设 IEEE 754 double,printf("%.17g\n", x) 将为您提供足够的数字来重新创建原始值。

Assuming IEEE 754 double, printf("%.17g\n", x) will give you enough digits to recreate the original value.

十雾 2024-10-17 20:28:53

一个两步过程:首先使用 二进制 float/double 序列化,然后应用 base 64 编码。结果不是人类可读的,但不会失去精度。

编辑:(感谢 fuzzyTew 和 dan04)

无损十进制和人类可读的表示可能是可能的,但需要更多的空间。

A two step process: First use binary float/double serialization and then apply base 64 encoding. The result is not human readable, but will not loose precision.

Edit: (Thanks to fuzzyTew and dan04)

Lossless decimal and human readable representation is probably possible, but would require much more space.

孤单情人 2024-10-17 20:28:53

您可以使用基数 64。这将允许您在文本文件中存储精确的字节值。

我还没有使用过它,但我发现了这个用于 C++ 的 Base 64 编码/解码

You could use base 64. This would allow you to store the exact byte values in a text file.

I haven't used it, but I found this base 64 encoding/decoding library for C++.

半世晨晓 2024-10-17 20:28:53

为了在 C++ 中无损失地打印长数字列表(在相同的结构中写入和读取),我使用这个(对于 doubles):

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

prints

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

To print long lists of numbers in C++ without loss (write and read in the same arquitecture) I use this (for doubles):

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

prints

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

In summary, in my case it is like setting:

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

In any case I can test if this works, by doing:

    std::istringstream iss(oss.str());
    double test; iss >> test;
    assert(test == original);
旧故 2024-10-17 20:28:53

我确信 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;
}

I was sure there was a special format specifier for printf (maybe %a?) that allowed printing the binary representation of a float, but I cannot find it..
However, you can try this:

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;
}
心病无药医 2024-10-17 20:28:53

试试这个:

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

然后将 s 写入文件。

Try this:

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

Then write s to file.

仙气飘飘 2024-10-17 20:28:53

你没有说为什么二进制是禁止的。对于您的应用程序来说,将二进制文件转换为十六进制 ASCII 字符串是否可行?

You don't say why binary is off limits. For your application would conqverting the binary to a hex ASCII string be workable?

⊕婉儿 2024-10-17 20:28:53

除了存储表示之外,像这样的东西怎么样?
特殊值如 -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] );
}

Storage representation aside, what about something like this.
Special values like -0, infinities, NaN etc would require special
handling though. Also I "forgot" to implement negative exponents.

#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] );
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文