C ++中double / float类型的二进制序列化的可移植性

C ++标准没有讨论浮点型和双精度型类型的底层布局,只讨论了它们应代表的值范围。 (对于带符号的类型也是如此,是两个人的赞美还是其他)

我的问题是:以便携式方式对POD类型(例如double和float)进行序列化/反序列化的技术是什么? 目前看来,唯一的方法就是用字面值表示值(如“ 123.456”中所示)。double的ieee754布局并非在所有体系结构上都是标准的。

asked 2020-06-30T02:19:08Z
9个解决方案
28 votes

Brian“ Beej Jorgensen” Hall在其《网络编程指南》中提供了一些代码,以将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;
}
Sylvain Defresne answered 2020-06-30T02:19:23Z
6 votes

可读格式有什么问题。

与二进制文件相比,它具有两个优点:

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

坏处:

  • 它不紧凑
    如果这是一个真正的问题,您可以随时将其压缩。
  • 提取/生成可能会稍微慢一些
    请注意,二进制格式可能也需要规范化(请参阅htonl()

要以全精度输出双精度:

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

好。 我不认为那是准确的。 它可能会失去精度。

Martin York answered 2020-06-30T02:20:44Z
5 votes

看一下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链接

user1016736 answered 2020-06-30T02:21:08Z
4 votes

只需将二进制IEEE754表示形式写入磁盘,并将其记录为存储格式(以及字节序)。 然后取决于实现,如有必要,将其转换为其内部表示。

TonyK answered 2020-06-30T02:21:28Z
2 votes

创建一个适当的序列化器/反序列化器接口以进行写入/读取。

然后,该界面可以有多种实现,您可以测试您的选项。

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

  • 如果体系结构直接支持,则写入/读取二进制块的IEEE754;如果体系结构不支持,则对二进制块进行解析
  • 文字:总是需要解析。
  • 无论您想到什么。

请记住-一旦有了该层,如果仅支持内部使用此格式的平台,则可以始终从IEEE754开始。 这样,只有在需要支持其他平台时,您才需要付出额外的努力! 不要做不需要做的工作。

Tobias Langner answered 2020-06-30T02:22:15Z
1 votes

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

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

peoro answered 2020-06-30T02:22:40Z
0 votes

我认为答案“取决于”您的特定应用程序及其性能概况。

假设您有一个低延迟的市场数据环境,那么坦率地说,使用字符串是愚蠢的。 如果您传达的信息是价格,那么加倍(以及价格的二进制表示)确实很难。 但是,如果您实际上并不关心性能,而您想要的只是可见性(存储,传输),那么字符串是理想的选择。

我实际上会选择浮点数/双精度数的整数尾数/指数表示形式-即,尽早将浮点数/双精度数转换为一对整数然后进行传输。 然后,您只需要担心整数的可移植性以及各种例程(例如hton()例程来为您处理转换)即可。 还要将所有内容存储在您最流行的平台的字节序中(例如,如果您仅使用linux,那么在big endian中存储内容有什么意义?)

Nim answered 2020-06-30T02:23:10Z
0 votes

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

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

资料来源:

[HTTPS://SQLite.org/双人床4/doc/trunk/呜呜呜/design.wiki]

[HTTPS://SQLite.org/双人床4/doc/trunk/呜呜呜/decimal.wiki]

Bernardo Ramos answered 2020-06-30T02:24:05Z
0 votes

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

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

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

J Lind answered 2020-06-30T02:24:35Z
translate from https://stackoverflow.com:/questions/4733147/portability-of-binary-serialization-of-double-float-type-in-c