C++ 如何将float转换为长度为4的字节数组(char *数组)?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14018894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to convert float to byte array of length 4 (array of char*)?
提问by PaolaJ.
How to convert float to byte array of length 4 (array of char*) ? I need to send over network some data, tcp, and need to send float as a byte array. ( I know precision to two decimal digits, so at the moment I on client side multiply by 100 and on server divide by 100 - basically convert to integer and then find bytes with & 0xff << operations). But it is ugly and can lost precision during time.
如何将 float 转换为长度为 4 的字节数组(char * 数组)?我需要通过网络发送一些数据,tcp,并且需要将浮点数作为字节数组发送。(我知道精度为两位十进制数字,所以目前我在客户端乘以 100,在服务器上除以 100 - 基本上转换为整数,然后使用 & 0xff << 操作查找字节)。但它很丑陋,并且随着时间的推移可能会失去精度。
回答by Kerrek SB
Reading anytype as a sequence of bytes is quite simple:
将任何类型作为字节序列读取非常简单:
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]);
}
Writingto a float from a network stream works similarly, only you'd leave out the const
.
从网络流写入浮点数的工作方式类似,只是您将const
.
It is always permitted to reinterpret any object as a sequence of bytes (any char
-type is permissible), and this expressly notan aliasing violation. Note that the binary representation of any type is of course platform dependent, so you should only use this for serialization if the recipient has the same platform.
始终允许将任何对象重新解释为字节序列(char
允许任何类型),这显然不是别名违规。请注意,任何类型的二进制表示当然都依赖于平台,因此如果接收者具有相同的平台,则仅应将其用于序列化。
回答by James Kanze
The first thing you have to do is to determine the format of the float in the network protocol. Just knowing that it is 4 bytes doesn't tell you much: IBM mainframe, Oracle Sparc and the usual PC all have four byte floats, but they have three different formats. Once you know the format, depending on it and your portability requirements, two different strategies can be used:
您要做的第一件事是确定网络协议中浮点数的格式。仅仅知道它是 4 个字节并不能说明什么:IBM 大型机、Oracle Sparc 和普通 PC 都有四个字节浮点数,但它们具有三种不同的格式。一旦您知道格式,根据它和您的可移植性要求,可以使用两种不同的策略:
If the format in the protocol is IEEE (the most frequent case),
and you don't have to be portable to machines which aren't IEEE
(Windows and most Unix are IEEE—most mainframes aren't),
then you can use type punning to convert the float to
a uint32_t
, and output that, using either:
如果协议中的格式是 IEEE(最常见的情况),并且您不必可移植到非 IEEE 的机器(Windows 和大多数 Unix 是 IEEE——大多数大型机不是),那么您可以使用输入 punning 将浮点数转换为 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 );
}
for big-endian (the usual network order), or:
对于 big-endian(通常的网络顺序),或:
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 );
}
for little-endian (used by some protocols). Which one you use will depend on the format defined for the protocol.
用于 little-endian(由某些协议使用)。您使用哪一种取决于为协议定义的格式。
To convert from float
to uint32_t
, you'll have to check your
compiler. Using memcpy
is the only method fully guaranteed by
the standard; the intent is that using
a reinterpret_cast<uint32_t&>
on the float work as well, and
most (all?) compiler also support using a union
.
要从 转换float
为uint32_t
,您必须检查编译器。使用memcpy
是标准完全保证的唯一方法;目的是reinterpret_cast<uint32_t&>
在浮点数上使用 a 也可以工作,并且大多数(所有?)编译器也支持使用union
.
If you need to be portable to mainframes as well, or the format is something other than IEEE, then you'll need to extract exponent, sign and mantissa from the float, and output each in the target format. Something like the following should work to output IEEE big-endian on anymachine (including mainframes which don't use IEEE), and should give you some idea:
如果您还需要可移植到大型机,或者格式不是 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
is a simple class which takes care of the usual
boilerplate and does error checking.) Of course, the various
manipulations for the output will be different if the output
format is not IEEE, but this should show the basic principles.
(If you need portability to some of the more exotic mainframes,
which don't support uint32_t
, you can replace it with any
unsigned integral type which is larger than 23 bits.)
(BytePutter
是一个简单的类,负责处理通常的样板文件并进行错误检查。)当然,如果输出格式不是 IEEE,则对输出的各种操作会有所不同,但这应该显示基本原理。(如果您需要可移植到一些不支持 的更奇特的大型机,uint32_t
您可以将其替换为任何大于 23 位的无符号整数类型。)
回答by myMan
Just overlay the data in one area, in C
只需将数据覆盖在一个区域中,在 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....