我正在尝试为在Win XP上运行的C ++程序编写一个byteswap例程。我正在使用Visual Studio 2008编译。这就是我想出的:
int byteswap(int v) // This is good
{
return _byteswap_ulong(v);
}
double byteswap(double v) // This doesn't work for some values
{
union { // This trick is first used in Quake2 source I believe :D
__int64 i;
double d;
} conv;
conv.d = v;
conv.i = _byteswap_uint64(conv.i);
return conv.d;
}
还有一个测试功能:
void testit() {
double a, b, c;
CString str;
for (a = -100; a < 100; a += 0.01) {
b = byteswap(a);
c = byteswap(b);
if (a != c) {
str.Format("%15.15f %15.15f %15.15f", a, c, a - c);
}
}
}
获取这些数字不匹配:
-76.789999999988126 -76.790000000017230 0.000000000029104 -30.499999999987718 -30.499999999994994 0.000000000007276 41.790000000014508 41.790000000029060 -0.000000000014552 90.330000000023560 90.330000000052664 -0.000000000029104
这是在阅读完之后:
How do I convert between big-endian and little-endian values in C++?
Little Endian - Big Endian Problem
你不能使用&lt;&lt;和&gt;&gt;顺便说一下(除非我弄错了?)
答案 0 :(得分:6)
虽然主存储器中的double
是64位,但在x86 CPU上,双精度寄存器的宽度为80位。因此,如果您的一个值全部存储在寄存器中,但另一个值通过主存储器往返并被截断为64位,这可以解释您所看到的小差异。
也许您可以通过获取地址(并打印它,以防止编译器优化它)来强制变量存在于主存储器中,但我不确定这是否可以保证工作。
答案 1 :(得分:4)
尝试3
好的,发现有更好的方法。另一种方式你必须担心打包/打包的顺序。这样你就不会:
// int and float
static void swap4(void *v)
{
char in[4], out[4];
memcpy(in, v, 4);
out[0] = in[3];
out[1] = in[2];
out[2] = in[1];
out[3] = in[0];
memcpy(v, out, 4);
}
// double
static void swap8(void *v)
{
char in[8], out[8];
memcpy(in, v, 8);
out[0] = in[7];
out[1] = in[6];
out[2] = in[5];
out[3] = in[4];
out[4] = in[3];
out[5] = in[2];
out[6] = in[1];
out[7] = in[0];
memcpy(v, out, 8);
}
typedef struct
{
int theint;
float thefloat;
double thedouble;
} mystruct;
static void swap_mystruct(void *buf)
{
mystruct *ps = (mystruct *) buf;
swap4(&ps->theint);
swap4(&ps->thefloat);
swap8(&ps->thedouble);
}
发送:
char buf[sizeof (mystruct)];
memcpy(buf, &s, sizeof (mystruct));
swap_mystruct(buf);
的Recv:
mystruct s;
swap_mystruct(buf);
memcpy(&s, buf, sizeof (mystruct));
答案 2 :(得分:3)
b = byteswap(a);
这是一个问题。交换字节后,该值不再是正确的双倍。当FPU将值标准化时,将其存储回double会导致细微问题。你必须将它存储回__int64(很长)。修改方法的返回类型。
答案 3 :(得分:0)
尝试2
好的,让它运转起来!汉斯帕斯特是对的。他们让我思考“不再适当的双重”评论。所以你不能将float换行到另一个float,因为它可能是一个不正确的格式,所以你必须将byteswap转换为一个char数组并取消换回。这是我使用的代码:
int pack(int value, char *buf)
{
union temp {
int value;
char c[4];
} in, out;
in.value = value;
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(buf, out.c, 4);
return 4;
}
int pack(float value, char *buf)
{
union temp {
float value;
char c[4];
} in, out;
in.value = value;
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(buf, out.c, 4);
return 4;
}
int pack(double value, char *buf)
{
union temp {
double value;
char c[8];
} in, out;
in.value = value;
out.c[0] = in.c[7];
out.c[1] = in.c[6];
out.c[2] = in.c[5];
out.c[3] = in.c[4];
out.c[4] = in.c[3];
out.c[5] = in.c[2];
out.c[6] = in.c[1];
out.c[7] = in.c[0];
memcpy(buf, out.c, 8);
return 8;
}
int unpack(char *buf, int *value)
{
union temp {
int value;
char c[4];
} in, out;
memcpy(in.c, buf, 4);
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(value, &out.value, 4);
return 4;
}
int unpack(char *buf, float *value)
{
union temp {
float value;
char c[4];
} in, out;
memcpy(in.c, buf, 4);
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(value, &out.value, 4);
return 4;
}
int unpack(char *buf, double *value)
{
union temp {
double value;
char c[8];
} in, out;
memcpy(in.c, buf, 8);
out.c[0] = in.c[7];
out.c[1] = in.c[6];
out.c[2] = in.c[5];
out.c[3] = in.c[4];
out.c[4] = in.c[3];
out.c[5] = in.c[2];
out.c[6] = in.c[1];
out.c[7] = in.c[0];
memcpy(value, &out.value, 8);
return 8;
}
一个简单的测试功能:
typedef struct
{
int theint;
float thefloat;
double thedouble;
} mystruct;
void PackStruct()
{
char buf[sizeof (mystruct)];
char *p;
p = buf;
mystruct foo, foo2;
foo.theint = 1;
foo.thefloat = 3.14f;
foo.thedouble = 400.5;
p += pack(foo.theint, p);
p += pack(foo.thefloat, p);
p += pack(foo.thedouble, p);
// Send or recv char array
p = buf;
p += unpack(p, &foo2.theint);
p += unpack(p, &foo2.thefloat);
p += unpack(p, &foo2.thedouble);
}
答案 4 :(得分:0)
(例如int16_t
,uint16_t
,uint32_t
,float
,double
等)
这是一种将效率从阵列的3个完整复制操作提高到阵列的1.5个完整复制操作的方法。另请参阅我留下的评论under your answer。
/// \brief Swap all the bytes in an array to convert from little-endian byte order
/// to big-endian byte order, or vice versa.
/// \note Works for arrays of any size. Swaps the bytes **in place** in the array.
/// \param[in,out] byte_array The array in which to swap the bytes in-place.
/// \param[in] len The length (in bytes) of the array.
/// \return None
void swap_bytes_in_array(uint8_t * byte_array, size_t len) {
size_t i_left = 0; // index for left side of the array
size_t i_right = len - 1; // index for right side of the array
while (i_left < i_right) {
// swap left and right bytes
uint8_t left_copy = byte_array[i_left];
byte_array[i_left] = byte_array[i_right];
byte_array[i_right] = left_copy;
i_left++;
i_right--;
}
}
用法:
double d;
// Swap the bytes in the double in place
swap_bytes_in_array((uint8_t*)(&d), sizeof(d));
uint64_t u64;
// swap the bytes in a uint64_t in place
swap_bytes_in_array((uint8_t*)(&u64), sizeof(u64));
但是请注意,@Hans Passant seems to be onto something here。尽管上面的方法在任何有符号或无符号 integer 类型上都可以完美地工作,并且对我来说似乎也可以在float
和double
上使用,但似乎在{{1} }。我认为这是因为当我将交换后的long double
存储回long double
变量中时,如果确定它不再是有效的long double
表示形式,则某些内容会自动更改交换字节或其他内容。我不太确定。
在许多64位系统上,long double
是16字节,因此解决方案是将long double
的交换版本保留在16字节数组中,而不要尝试使用或强制转换将其从long double
16字节数组返回到long double
,直到任一A)它已发送到接收器(系统的字节序相反,因此现在状态良好)和/或B)再次交换字节,因此它又是有效的uint8_t
。
请记住以上几点,以防您也遇到long double
或float
类型的问题,就像我看到的只有double
类型一样。