尽管大端计算机的使用并不是很广泛,但我希望以独立的格式存储双数据类型。
对于int,这非常简单,因为位移使得这非常方便。
int number;
int size=sizeof(number);
char bytes[size];
for (int i=0; i<size; ++i)
bytes[size-1-i] = (number >> 8*i) & 0xFF;
此代码snipet以大端格式存储该数字,尽管它正在运行的机器上。 为双重执行此操作的最优雅方式是什么?
答案 0 :(得分:7)
可移植性和考虑格式的最佳方法是分别序列化/反序列化尾数和指数。为此,您可以使用frexp()/ ldexp()函数。
例如,要序列化:
int exp;
unsigned long long mant;
mant = (unsigned long long)(ULLONG_MAX * frexp(number, &exp));
// then serialize exp and mant.
然后反序列化:
// deserialize to exp and mant.
double result = ldexp ((double)mant / ULLONG_MAX, exp);
答案 1 :(得分:5)
要做的优雅事情是将字节序问题限制在尽可能小的范围内。这个狭窄的范围是您的程序与外部世界之间的I / O边界。例如,将二进制数据发送到/从其他应用程序接收二进制数据的函数需要知道endian问题,将二进制数据写入/从某些数据文件读取二进制数据的函数也是如此。让这些界面认识到表示问题。
让其他一切幸福地对这个问题一无所知。在其他地方使用本地表示。将双精度浮点数表示为double
而不是8字节的数组,将32位整数表示为int
或int32_t
,而不是4字节的数组,等等。在整个代码中处理字节序问题会导致代码膨胀,容易出错,并且很难看。
答案 2 :(得分:1)
同样的。任何数字对象(包括double)最终都是几个字节,根据字节顺序以特定顺序进行解释。因此,如果您恢复字节的顺序,您将在反向字节顺序中获得完全相同的值。
答案 3 :(得分:1)
char *src_data;
char *dst_data;
for (i=0;i<N*sizeof(double);i++) *dst_data++=src_data[i ^ mask];
// where mask = 7, if native == low endian
// mask = 0, if native = big_endian
优雅在于mask
,它还处理短整数类型:如果目标和源字节序不同,则它的sizeof(elem)-1。
答案 4 :(得分:0)
不是非常便携,标准违反了,但是这样的话:
std::array<unsigned char, 8> serialize_double( double const* d )
{
std::array<unsigned char, 8> retval;
char const* begin = reinterpret_cast<char const*>(d);
char const* end = begin + sizeof(double);
union
{
uint8 i8s[8];
uint16 i16s[4];
uint32 i32s[2];
uint64 i64s;
} u;
u.i64s = 0x0001020304050607ull; // one byte order
// u.i64s = 0x0706050403020100ull; // the other byte order
for (size_t index = 0; index < 8; ++index)
{
retval[ u.i8s[index] ] = begin[index];
}
return retval;
}
可能会处理一个具有8位字符,8字节双精度和任何疯狂屁股字节排序的平台(例如,单词中的大端,但对于64位值的单词之间的小端,例如)。
现在,这并未涵盖双精度的字节顺序与64位整数的不同。
更简单的方法可能是将double转换为64位无符号值,然后像输出任何其他int一样输出。
答案 5 :(得分:0)
void reverse_endian(double number, char (&bytes)[sizeof(double)])
{
const int size=sizeof(number);
memcpy(bytes, &number, size);
for (int i=0; i<size/2; ++i)
std::swap(bytes[i], bytes[size-i-1]);
}