C++中双/浮点型二进制序列化的可移植性

46 c++ double serialization portability ieee-754

C++标准没有讨论float和double类型的底层布局,只讨论它们应该表示的值的范围.(对于签名类型也是如此,这是两个恭维还是别的)

我的问题是:用于以可移植方式序列化/反序列化POD类型(如double和float)的技术是什么?目前,似乎唯一的方法是将值表示为字面意义(如"123.456"),double的ieee754布局在所有体系结构上都不是标准的.

Syl*_*sne 28

Brian"Beej Jorgensen"Hall在他的网络编程指南中给出了一些代码float(resp.double)到uint32_t(resp.uint64_t),以便能够通过网络在两台机器之间安全地传输它,这两台机器可能不同意它们的表示.它有一些限制,主要是它不支持NaN和无穷大.

这是他的包装功能:

#define pack754_32(f) (pack754((f), 32, 8))
#define pack754_64(f) (pack754((f), 64, 11))

uint64_t pack754(long double f, unsigned bits, unsigned expbits)
{
    long double fnorm;
    int shift;
    long long sign, exp, significand;
    unsigned significandbits = bits - expbits - 1; // -1 for sign bit

    if (f == 0.0) return 0; // get this special case out of the way

    // check sign and begin normalization
    if (f < 0) { sign = 1; fnorm = -f; }
    else { sign = 0; fnorm = f; }

    // get the normalized form of f and track the exponent
    shift = 0;
    while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
    while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
    fnorm = fnorm - 1.0;

    // calculate the binary form (non-float) of the significand data
    significand = fnorm * ((1LL<<significandbits) + 0.5f);

    // get the biased exponent
    exp = shift + ((1<<(expbits-1)) - 1); // shift + bias

    // return the final answer
    return (sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你需要NaN,无穷大和非规范化数字应该不难.此外,这段代码是公共领域,这使它成为一个很好的答案. (6认同)
  • 基于`frexp`的方法是否会比重复的浮点除法/乘法更快?`frexp`在一次调用中给你`exp`和`fnorm`.请记住,IEEE 754 double有11位指数,所以你可以将数字除以2或几倍. (3认同)

Mar*_*ork 6

人类可读格式有什么问题.

它比二进制有两个优点:

  • 它是可读的
  • 它是便携式的
  • 它使支持变得非常简单
    (因为您可以要求用户在他们喜欢的编辑器中查看它甚至单词)
  • 它很容易修复
    (或在错误情况下手动调整文件)

坏处:

  • 它不紧凑
    如果这是一个真正的问题,你可以随时拉链.
  • 提取/生成可能稍慢一些
    注意二进制格式也可能需要进行标准化(请参阅参考资料htonl())

要以完全精度输出double:

double v = 2.20;
std::cout << std::setprecision(std::numeric_limits<double>::digits) << v;
Run Code Online (Sandbox Code Playgroud)

好.我不相信这是完全准确的.它可能会失去精确度.

  • @Martin:文字表示解码非常慢,我正在研究一个处理非常大的时间序列的系统,紧凑,精确和高速可解码表示是必须的 - 可移植性也很重要. (9认同)
  • 附加缺点:不精确.应用程序之间的重要性差别很大. (6认同)
  • @Maxim:所以你说的是它不适用于Windows或当前的C++标准. (2认同)

use*_*736 5

看一下glib 2中的(旧)gtypes.h文件实现-它包括以下内容:

#if G_BYTE_ORDER == G_LITTLE_ENDIAN
union _GFloatIEEE754
{
  gfloat v_float;
  struct {
    guint mantissa : 23;
    guint biased_exponent : 8;
    guint sign : 1;
  } mpn;
};
union _GDoubleIEEE754
{
  gdouble v_double;
  struct {
    guint mantissa_low : 32;
    guint mantissa_high : 20;
    guint biased_exponent : 11;
    guint sign : 1;
  } mpn;
};
#elif G_BYTE_ORDER == G_BIG_ENDIAN
union _GFloatIEEE754
{
  gfloat v_float;
  struct {
    guint sign : 1;
    guint biased_exponent : 8;
    guint mantissa : 23;
  } mpn;
};
union _GDoubleIEEE754
{
  gdouble v_double;
  struct {
    guint sign : 1;
    guint biased_exponent : 11;
    guint mantissa_high : 20;
    guint mantissa_low : 32;
  } mpn;
};
#else /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
#error unknown ENDIAN type
#endif /* !G_LITTLE_ENDIAN && !G_BIG_ENDIAN */
Run Code Online (Sandbox Code Playgroud)

glib链接


peo*_*oro 1

您应该将它们转换为您始终能够使用的格式,以便重新创建浮点数/双精度数。

这可以使用字符串表示形式,或者,如果您需要占用更少空间的内容,请以 ieee754(或您选择的任何其他格式)表示您的数字,然后像处理字符串一样解析它。

  • 是否有任何库可以将 double 转换为特定的二进制格式?目前我们所做的就是将内存中的布局写入磁盘,这是可以的,但在异构环境中它不会很好地工作。 (2认同)