我最近喜欢阅读Beej's Guide to Network Programming。在section 7.4中,他讨论了与发送彩信有关的问题。他提供了一个简单(幼稚)的解决方案,其中他通过将浮点数转换为uint32_t来“打包”浮点数:

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_tntohf(“解包器”)。

最佳答案

是的,您还必须按定义的顺序编码(marshal)数据;最简单的方法是使用htonl

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

这些天实际上几乎所有的CPU都使用IEEE-754格式的浮点数,但是我也不会使用Beej的第二种解决方案,因为它的速度太慢了。标准库函数frexpldexp将在 double 和相应的尾数以及整数二进制指数之间可靠地转换。或者,如果您喜欢该界面,则可以使用ilogb*scalb*。您可以通过FLT_MANT_DIGDBL_MANT_DIGLDBL_MANT_DIG(在float.h中)宏在主机上找到尾数的适当位长。 [见注1]

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

笔记:

  • 实现提示:frexp返回0.5到1.0之间的尾数,但您真正想要的是整数,因此您应将尾数按正确的2的幂进行缩放,然后从frexp返回的二进制指数中减去该尾数。只要您可以传输任意精度的整数,结果就不会真正依赖于精度,因此您无需区分floatdouble或其他某种二进制表示形式。
  • 10-07 19:16
    查看更多