我正在尝试从原始字节数组中读取浮点值。我有一个指向原始字节数组的指针,我想读取与原始字节相关联的浮点值。我正在使用联合数据结构来读取浮点值,但是我无法读取正确的值。
// Floating point value: 0x3F800000 (floating point value 1.0)
char * c = "\u003F\u0080\u0000\u0000";
union char4_or_float {
char element[4];
float val;
} cf;
cf.element[0] = c[0];
cf.element[1] = c[1];
cf.element[2] = c[2];
cf.element[3] = c[3];
printf("%f", cf.val);
预期结果:1.0,返回输出:0.0
我想知道这段代码是否正确。如果没有,你能告诉我如何解决它吗?另外,如果有它不起作用的场景,你能提到它们吗?
谢谢。
你有两个问题:
const char * c = "\x3F\x80\x00\x00";
代替cf.element[0] = c[3];
cf.element[1] = c[2];
cf.element[2] = c[1];
cf.element[3] = c[0];
虽然以上所有都依赖于未定义的行为,但 memcpy 会更简单和合法:
#include <cstdio>
#include <cstring>
int main()
{
const char * c = "\x00\x00\x80\x3f";
float f;
std::memcpy(&f, c, sizeof(f));
printf("%f", f);
}
几个问题:
对于文字,您应该将 char 定义为 const char*
Unicode 文字应以“u”开头并分配给
const char16_t*
而不是 const char*
您可以使用带有“x”前缀的文字
const char*
总是使用 memcpy 来避免别名。按照标准,执行“联合方式”通常是未定义的行为,尽管它过去工作正常。
PC 是小端字节序所以字节的顺序是颠倒的
这是我的看法:
#include <cstdio>
#include <cstring>
#include <cstdint>
int main()
{
const char * c = "\x00\x00\x80\x3f";
const char16_t * d = u"\u0000\u3f80";
float val;
memcpy( &val, c, sizeof(val));
printf("%f\n", val);
memcpy( &val, d, sizeof(val));
printf("%f\n", val);
uint32_t ival;
memcpy(&ival,c,sizeof(ival));
printf( "%08x\n", ival );
memcpy(&ival,c,sizeof(ival));
printf( "%08x\n", ival );
}
这打印
1.000000
1.000000
3f800000
3f800000
Godbolt 链接:https://godbolt.org/z/nsrGbaYn1
您不能以可移植的方式使用联合进行类型双关,标准 C++ 不允许这样做。此外,您需要注意字节顺序。
下面我从一个
float
开始。这可以被视为一个字节数组。我将它复制到第二个char
数组。我这样做是为了获得字节 -> 浮点部分的正确输入,然后通过将字节 memcopying 到 float
:
#include <iostream>
#include <cstring>
int main() {
// prepare the right input with right endianess
float x = 1.0;
char* ptr = reinterpret_cast<char*>(&x);
char* ptr_copy = new char[sizeof(float)];
for (unsigned i=0;i<sizeof(float);++i) {
std::cout << static_cast<unsigned>(ptr[i]) << " ";
ptr_copy[i] = ptr[i];
}
// now ptr_copy is the array of bytes that can be
// transformed to a float via memcpy
float y;
std::memcpy(&y,ptr_copy,sizeof(float));
std::cout << "\n" << y;
}
您遇到的一个问题是您尝试创建的字符串不是您实际创建的字符串。您正在使用 \u,它被解析为 unicode 字符(在您的情况下无论如何都是无效的)。如果你试图在内存中为 0x3F800000 创建原始字节,你应该像这样转义它们:
"\x3f\x80\x00\x00"
但这会出现第二个问题,您正在处理哪种字节序(可能是小字节序),因为您将原始字节指定为内存中的连续字节,您必须意识到这一点。
"\x3f\x80\x00\x00" 将在大端生成 0x3f800000
"\x00\x00\x80\x3f" 将在小端中产生 0x3f800000
因此更改该行将使您的代码正常工作(如果您使用的是小端平台)
// char * c = "\u003F\u0080\u0000\u0000";
char * c = "\x00\x00\x80\x3f"; // little endian for float 1
当你将这个问题标记为 C++ 时,我会提到你将原始字节读入浮点数的方式应该是这样的:
char *rawbytes="...";
float f=*reinterpret_cast<float*>(rawbytes);
如果原始字节的字节顺序与您的系统不同,则必须交换字节。直到 C++23 才为它内置了一个,所以你可能应该使用这样的东西:
template<typename T, typename std::enable_if<std::is_integral_v<T> && sizeof(T)==4,int>::type=0>
constexpr inline void binarySwap(T &value) {
std::uint32_t tmp = ((value << 8) & 0xFF00FF00) | ((value >> 8) & 0xFF00FF);
value = (tmp << 16) | (tmp >> 16);
}
当然,字节交换功能将取决于您正在处理的浮点类型的大小。根据你的问题,我在这里展示了 32 位版本。