我想将浮点数从小端转换为大端,但无法做到。我已经成功转换了 int 数字的字节顺序,但是有人可以帮忙处理浮点数吗
2 回答
#include <cstring> // for std::memcpy
#include <algorithm> // for std::reverse
#include <iterator> // For C++11 std::begin() and std::end()
// converting from float to bytes for writing out
float f = 10.0;
char c[sizeof f];
std::memcpy(c,&f,sizeof f);
std::reverse(std::begin(c),std::end(c)); // begin() and end() are C++11. For C++98 say std::reverse(c,c + sizeof f);
// ... write c to network, file, whatever ...
走向另一个方向:
char c[] = { 41, 36, 42, 59 };
static_assert(sizeof(float) == sizeof c,"");
std::reverse(std::begin(c),std::end(c));
float f;
std::memcpy(&f,c,sizeof f);
浮点值的表示是实现定义的,因此由此产生的值在不同的实现之间可能不同。也就是说,交换的 10.0 字节可能是 1.15705e-041 或其他东西,或者它可能根本不是一个有效的浮点数。
但是,任何使用 IEEE 754 的实现(大多数都这样做,并且您可以通过查看是否std::numeric_limits<float>.is_iec559
为真来检查),应该会给您相同的结果。(std::numeric_limits
来自#include <limits>
。)
上面的代码将浮点数转换为字节,修改字节,然后将这些字节转换回浮点数。如果您有一些要读取为浮点数的字节值,那么您可以将 char 数组的值设置为您的字节,然后使用memcpy()
如上所示(通过后面的行std::reverse()
)将这些字节放入f
.
通常人们会推荐使用 reinterpret_cast 来处理这类事情,但我认为避免强制转换是件好事。人们经常错误地使用它们并在没有意识到的情况下获得未定义的行为。在这种情况下 reinterpret_cast 可以合法使用,但我仍然认为最好避免使用它。
虽然它确实将 4 行减少到 1...
std::reverse(reinterpret_cast<char*>(&f),reinterpret_cast<char*>(&f) + sizeof f);
这里有一个例子说明为什么你不应该使用 reinterpret_cast。以下可能会起作用,但可能会导致未定义的行为。由于它有效,您可能甚至不会注意到您做错了什么,这是可能的最不理想的结果之一。
char c[] = { 41, 36, 42, 59 };
static_assert(sizeof(float) == sizeof c,"");
float f = *reinterpret_cast<float*>(&c[0]);
做这些事情的正确方法是使用联合。
union float_int {
float m_float;
int32_t m_int;
};
这样您就可以将浮点数转换为整数,并且由于您已经知道如何转换整数字节序,所以一切都很好。
对于双倍,它是这样的:
union double_int {
double m_float;
int64_t m_int;
};
int32_t 和 int64_t 通常在 stdint.h 中可用,boost 提供此类,Qt 有自己的定义集。只要确保整数的大小正好等于浮点数的大小。在某些系统上,您还定义了 long double :
union double_int {
long double m_float;
int128_t m_int;
};
如果 int128_t 不起作用,您可以像这样使用结构:
union long_double_int {
long double m_float;
struct {
int32_t m_int_low;
int32_t m_int_hi;
};
};
这可能会让您认为在所有情况下,您都可以使用字节,而不是使用 int:
union float_int {
float m_float;
unsigned char m_bytes[4];
};
那时您发现不需要进行此类转换时使用的所有常用班次......因为您还可以声明:
union char_int {
int m_int;
unsigned char m_bytes[4];
};
现在你的代码看起来很简单:
float_int fi;
char_int ci;
fi.m_float = my_float;
ci.m_bytes[0] = fi.m_bytes[3];
ci.m_bytes[1] = fi.m_bytes[2];
ci.m_bytes[2] = fi.m_bytes[1];
ci.m_bytes[3] = fi.m_bytes[0];
// now ci.m_int is the float in the other endian
fwrite(&ci, 1, 4, f);
[...snip...]
fread(&ci, 1, 4, f);
// here ci.m_int is the float in the other endian, so restore:
fi.m_bytes[0] = ci.m_bytes[3];
fi.m_bytes[1] = ci.m_bytes[2];
fi.m_bytes[2] = ci.m_bytes[1];
fi.m_bytes[3] = ci.m_bytes[0];
my_float = fi.m_float;
// now my_float was restored from the file
显然,在此示例中交换了字节顺序。如果您的程序要在 LITTLE_ENDIAN 和 BIG_ENDIAN 计算机上编译,您可能还需要知道是否确实需要进行这种交换(检查 BYTE_ENDIAN。)