8

如何将浮点数转换为长度为 4 的字节数组(char* 数组)?我需要通过网络发送一些数据,tcp,并且需要将浮点数作为字节数组发送。(我知道两位小数的精度,所以目前我在客户端乘以 100,在服务器上除以 100 - 基本上转换为整数,然后使用 & 0xff << 操作查找字节)。但它很丑陋,并且会随着时间的推移而失去精确度。

4

3 回答 3

16

将任何类型读取为字节序列非常简单:

float f = 0.5f;

unsigned char const * p = reinterpret_cast<unsigned char const *>(&f);

for (std::size_t i = 0; i != sizeof(float); ++i)
{
    std::printf("The byte #%zu is 0x%02X\n", i, p[i]);
}

从网络流写入浮点数的工作方式类似,只是你会省略const.

总是允许将任何对象重新解释为字节序列(任何char类型都是允许的),这显然不是别名违规。请注意,任何类型的二进制表示当然是依赖于平台的,因此如果接收者具有相同的平台,则应仅将其用于序列化。

于 2012-12-24T08:27:46.790 回答
6

您要做的第一件事是确定网络协议中浮点数的格式。仅仅知道它是 4 字节并不能说明太多:IBM 大型机、Oracle Sparc 和普通 PC 都有 4 字节浮点数,但它们具有三种不同的格式。一旦您知道格式,根据它和您的可移植性要求,可以使用两种不同的策略:

如果协议中的格式是 IEEE(最常见的情况),并且您不必移植到非 IEEE 的机器上(Windows 和大多数 Unix 是 IEEE,大多数大型机不是),那么您可以使用键入 punning 将 float 转换为 a uint32_t,并使用以下任一方法输出:

std::ostream&
output32BitUInt( std::ostream& dest, uint32_t value )
{
    dest.put( (value >> 24) & 0xFF );
    dest.put( (value >> 16) & 0xFF );
    dest.put( (value >>  8) & 0xFF );
    dest.put( (value      ) & 0xFF );
}

对于大端(通常的网络顺序),或者:

std::ostream&
output32BitUInt( std::ostream& dest, uint32_t value )
{
    dest.put( (value      ) & 0xFF );
    dest.put( (value >>  8) & 0xFF );
    dest.put( (value >> 16) & 0xFF );
    dest.put( (value >> 24) & 0xFF );
}

对于 little-endian(某些协议使用)。您使用哪一种取决于为协议定义的格式。

要从 转换floatuint32_t,您必须检查您的编译器。使用memcpy是标准完全保证的唯一方法;目的是reinterpret_cast<uint32_t&>在浮动上使用 a 也可以,并且大多数(全部?)编译器也支持使用 a union

如果您还需要移植到大型机,或者格式不是 IEEE,那么您需要从浮点数中提取指数、符号和尾数,并以目标格式输出。像下面这样的东西应该可以在任何机器上输出 IEEE big-endian(包括不使用 IEEE 的大型机),并且应该给你一些想法:

oxdrstream&
oxdrstream::operator<<(
    float               source )
{
    BytePutter          dest( *this ) ;
    bool                isNeg = source < 0 ;
    if ( isNeg ) {
        source = - source ;
    }
    int                 exp ;
    if ( source == 0.0 ) {
        exp = 0 ;
    } else {
        source = ldexp( frexp( source, &exp ), 24 ) ;
        exp += 126 ;
    }
    uint32_t               mant = source ;
    dest.put( (isNeg ? 0x80 : 0x00) | exp >> 1 ) ;
    dest.put( ((exp << 7) & 0x80) | ((mant >> 16) & 0x7F) ) ;
    dest.put( mant >> 8 ) ;
    dest.put( mant      ) ;
    return *this ;
}

BytePutter是一个简单的类,负责处理通常的样板文件并进行错误检查。)当然,如果输出格式不是 IEEE,则​​输出的各种操作会有所不同,但这应该显示基本原理。(如果您需要移植到一些不支持的更奇特的大型机,uint32_t您可以将其替换为任何大于 23 位的无符号整数类型。)

于 2012-12-24T10:49:14.280 回答
5

只需将数据覆盖在一个区域中,在 C 中

union dataUnion {  
    float f;  
    char fBuff[sizeof(float)];  
}  
// to use:  
dataUnion myUnion;  
//  
myUnion.f = 3.24;  
for(int i=0;i<sizeof(float);i++)  
    fputc(myUnion.fbuff[i],fp); // or what ever stream output....  
于 2014-02-20T05:23:00.307 回答