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

时间:2011-01-19 08:27:33

标签: c++ serialization double portability ieee-754

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

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

9 个答案:

答案 0 :(得分:27)

Brian“Beej Jorgensen”Hall在他的Guide to Network Programming中提供了一些代码,以便将float(分别为double)打包到uint32_t(分别为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;
}

答案 1 :(得分:5)

人类可读格式出了什么问题。

它比二进制有一些优势:

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

缺点:

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

以完全精度输出双精度:

double v = 2.20;
std::cout << std::setprecision(std::numeric_limits<double>::digits) << v;

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

答案 2 :(得分:4)

只需将二进制IEEE754表示写入磁盘,并将其记录为存储格式(以及字节顺序)。然后,如果需要,可以将其转换为内部表示。

答案 3 :(得分:4)

看看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 */

glib link

答案 4 :(得分:2)

创建一个适当的串行器/反序列化器接口,用于写入/读取它。

接口可以有多个实现,您可以测试您的选项。

如前所述,显而易见的选择是:

  • IEEE754,如果架构直接支持,则写入/读取二进制块,如果架构不支持则解析它
  • 文字:总是需要解析。
  • 无论你能想到什么,你都能想到。

请记住 - 一旦有了这一层,如果你只支持内部使用这种格式的平台,你总是可以从IEEE754开始。这样,只有当您需要支持不同的平台时,您才会有额外的努力!不要做你不需要的工作。

答案 5 :(得分:1)

您应该将它们转换为您始终可以使用的格式,以便重新创建浮动/双打。

这可以使用字符串表示,或者,如果您需要占用较少空间的内容,则以ieee754(或您选择的任何其他格式)表示您的号码,然后解析,就像使用字符串。

答案 6 :(得分:0)

我认为答案“取决于”您的特定应用程序及其性能配置文件。

假设你有一个低延迟的市场数据环境,那么使用字符串就是坦率的愚蠢。如果您传达的信息是价格,那么双倍(以及它们的二进制表示)确实很难处理。在那里,如果你真的不关心性能,你想要的是可见性(存储,传输),那么字符串是理想的候选者。

我实际上会选择浮点数/双精度的积分尾数/指数表示 - 即在最早的时候,将float / double转换为一对整数,然后传输它。然后,您只需要担心整数的可移植性以及各种例程(例如hton()例程来为您处理转换)。还将所有内容存储在最流行的平台的endianess中(例如,如果你只使用linux,那么以big endian存储东西的重点是什么?)

答案 7 :(得分:0)

SQLite4使用新格式存储双精度数和浮点数

  
      
  • 即使在缺乏对IEEE 754 binary64浮点数的支持的平台上,它也能可靠且一致地工作。
  •   
  • 通常可以精确地进行货币计算而不进行舍入。
  •   
  • 可以准确表示任何有符号或无符号的64位整数。
  •   
  • 浮点范围和精度超过IEEE 754 binary64浮点数。
  •   
  • 正负无穷大和NaN(非数字)具有明确定义的表示。
  •   

来源:

https://sqlite.org/src4/doc/trunk/www/design.wiki

https://sqlite.org/src4/doc/trunk/www/decimal.wiki

答案 8 :(得分:0)

找到了这个旧线程。缺少一种解决多种情况的解决方案-使用固定点,在两端使用内置强制转换传递具有已知比例因子的整数。因此,您根本不必理会基础浮点表示。

当然有缺点。该解决方案假定您可以具有固定的缩放比例,并且仍然可以获得特定应用程序所需的范围和分辨率。此外,您在序列化端将浮点转换为固定点,然后在反序列化时转换回固定点,从而引入了两个舍入错误。 但是,多年来,我发现固定点几乎可以满足我的所有需求,而且速度也相当快。

固定点的典型情况是嵌入式系统或其他设备的通信协议。