3

我最近很喜欢阅读Beej 的网络编程指南。在第 7.4 节中,他谈到了与发送浮点数相关的问题。他提供了一个简单(且幼稚)的解决方案,通过将它们转换为uint32_t's 来“打包”浮动:

uint32_t htonf(float f)
{
    uint32_t p;
    uint32_t sign;

    if (f < 0) { sign = 1; f = -f; }
    else { sign = 0; }

    p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // whole part and sign
    p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // fraction

    return p;
}

float ntohf(uint32_t p)
{
    float f = ((p>>16)&0x7fff); // whole part
    f += (p&0xffff) / 65536.0f; // fraction

    if (((p>>31)&0x1) == 0x1) { f = -f; } // sign bit set

    return f;
}

我应该在发送之前htonf通过标准运行打包的浮点数(即 的结果)htons吗?如果没有,为什么不呢?

据我所知,Beej 没有提到这一点。我问的原因是,如果数据在发送之前没有转换为网络字节顺序,我无法理解接收机器如何可靠地重建uint32_t要传递给(“解包器”)的 s 。ntohf

4

2 回答 2

5

是的,您还必须按定义的顺序整理数据;最简单的方法是使用htonl.

但是,除了教育目的之外,我真的建议远离这段代码。它的范围非常有限,并且默默地破坏了大多数数字。此外,它的功能确实是不必要的复杂。您也可以将浮点数乘以 65536 并将其转换为 int 发送;转换为浮点数并除以 65536.0 以接收。(正如评论中所指出的,指南的代码是否具有教育意义甚至值得怀疑:我会说它具有教育意义,因为批评它和/或将其与好的代码进行比较会教给你一些东西:如果没有别的,那就不是网络上闪闪发光的一切都是金子。)

现在几乎所有的 CPU 都使用 IEEE-754 格式的浮点数,但我也不会使用 Beej 的第二个解决方案,因为它太慢了;标准库函数frexp并将ldexp可靠地在双精度和相应的尾数和整数二进制指数之间进行转换。或者,如果您更喜欢该界面,您可以使用ilogb*and 。您可以通过宏,和(in )scalb*找到主机上尾数的适当位长。[见注 1]FLT_MANT_DIGDBL_MANT_DIGLDBL_MANT_DIGfloat.h

正确编码浮点数据传输是开始理解浮点表示的好方法,这绝对是值得的。但是,如果您只是想通过网络传输浮点数并且您没有一些特殊的处理器要支持,我建议您只发送浮点数的原始位或双倍作为 4 字节或 8 字节整数(以您选择的任何字节顺序作为标准),并将自己限制为 IEEE-754 32 位和 64 位表示。


笔记:

  1. 实现提示:frexp返回一个介于 0.5 和 1.0 之间的尾数,但您真正想要的是一个整数,因此您应该将尾数按 2 的正确幂缩放,然后从返回的二进制指数中减去它frexp。只要您可以传输任意精度整数,结果就不是真正依赖于精度的,因此您不需要区分floatdouble或其他二进制表示。
于 2013-09-16T15:05:44.627 回答
1

运行它们htonl(反之亦然),而不是htons.

这两个函数htonfntohf,就它们而言是可以的(即,根本不是很远),但它们的名称具有误导性。它们生成定点 32 位表示,其中 31 位分为:15 位整数,16 位小数。剩余的位保持符号。此值在主机的内部表示中。(您可以htonl在函数本身中执行 etc 来解决此问题。)

请注意,任何绝对值达到或超过 32768 或小于 2 -16 (.0000152587890625) 的浮点数都将在“网络化”过程中被破坏,因为它们不适合 15.16 格式。

(编辑添加:最好使用打包的网络生成器。即使像 Sun RPC XDR 例程一样古老的东西也能正确编码浮点数。)

于 2013-09-16T15:10:32.650 回答