C++ XOR 交换浮点值
C++ XOR swap on float values
是否可以在 C++ 中使用具有浮点值的 XOR 交换算法?
但是维基百科说:
XOR bitwise operation to swap values of distinct variables having the same data type
但我有点困惑。使用此代码:
void xorSwap (int* x, int* y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
int main() {
float a = 255.33333f;
float b = 0.123023f;
xorSwap(reinterpret_cast<int*>(&a), reinterpret_cast<int*>(&b));
std::cout << a << ", " << b << "\n";
return 0;
}
它似乎可行(至少在 gcc 下是这样),但我担心如果需要,是否允许这种做法?
从技术上讲,您提出的问题 是 可能的,但正如 IInspectable 明确评论的那样,它会导致 UB(未定义行为)。
无论如何,我建议您改用 std::swap,它是一个模板,通常专门用于特定的数据类型,并且设计得很好。
如果 int
与 float
大小相同,它将在任何合理的体系结构上实际工作。 float 中的内存是一组位,您解释这些位并使用 xor 操作完全交换它们。然后您可以再次使用这些位作为正确的 float
s。您引用的引述仅表示您交换的两个值需要属于同一类型,并且都是 int
s.
但是,在某些体系结构上,这可能会导致在不同类型的寄存器之间移动,或者显式地将寄存器刷新到内存中。如今,您将在几乎所有具有合理优化编译器的合理架构上看到的是,使用 std::swap
或带有临时变量的表达式的显式交换实际上更快。
即你应该写:
float a = 255.33333f;
float b = 0.123023f;
float tmp = a;
a = b;
b = tmp;
或者最好是:
float a = 255.33333f;
float b = 0.123023f;
std::swap(a,b);
如果您的体系结构的标准库作者确定异或交换确实有益,那么您应该希望最后一种形式会使用它。就在不必要的神秘实现中隐藏意图而言,异或交换是一个典型的坏习语。它仅在严重 register-starved 优化器不佳的情况下才有效。
您的代码调用了未定义的行为。在 C 或 C++ 中将 float*
转换为 int*
并照此使用是不合法的。 reinterpret_cast
应该用于在具有兼容布局的不相关结构之间进行转换,或者在类型指针和 void*
.
之间临时进行转换
哦,在这种特殊情况下,UB 不仅仅是学术问题。编译器可能会注意到 xorSwap()
没有触及任何 float
s,执行语言别名规则允许的优化,并打印出 a
和 [= 的原始值17=] 而不是交换值。这甚至还没有进入 int
和 float
具有不同大小或对齐方式的架构。
如果您想安全地执行此操作,则必须 memcpy()
从浮点数到无符号字符数组,在循环中执行 XOR,然后 memcpy()
返回。这当然会使操作比正常交换慢。当然,xor-based 交换已经比正常交换慢了。
是:
a) 在编译器允许的情况下可能。
b) 标准没有定义行为的操作(即未定义的行为)
c) 在 gcc 上,实际上比准确说明你想要的更有效:
给定:
void xorSwap (unsigned int* x, unsigned int* y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
void swapit3(float& a, float&b)
{
xorSwap(reinterpret_cast<unsigned int*>(&a), reinterpret_cast<unsigned int*>(&b));
}
结果:
swapit3(float&, float&): # @swapit3(float&, float&)
mov eax, dword ptr [rdi]
xor eax, dword ptr [rsi]
mov dword ptr [rdi], eax
xor eax, dword ptr [rsi]
mov dword ptr [rsi], eax
xor dword ptr [rdi], eax
ret
而这个:
void swapit2(float& a, float&b)
{
std::swap(a,b);
}
结果:
swapit2(float&, float&): # @swapit2(float&, float&)
mov eax, dword ptr [rdi]
mov ecx, dword ptr [rsi]
mov dword ptr [rdi], ecx
mov dword ptr [rsi], eax
ret
是否可以在 C++ 中使用具有浮点值的 XOR 交换算法?
但是维基百科说:
XOR bitwise operation to swap values of distinct variables having the same data type
但我有点困惑。使用此代码:
void xorSwap (int* x, int* y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
int main() {
float a = 255.33333f;
float b = 0.123023f;
xorSwap(reinterpret_cast<int*>(&a), reinterpret_cast<int*>(&b));
std::cout << a << ", " << b << "\n";
return 0;
}
它似乎可行(至少在 gcc 下是这样),但我担心如果需要,是否允许这种做法?
从技术上讲,您提出的问题 是 可能的,但正如 IInspectable 明确评论的那样,它会导致 UB(未定义行为)。
无论如何,我建议您改用 std::swap,它是一个模板,通常专门用于特定的数据类型,并且设计得很好。
如果 int
与 float
大小相同,它将在任何合理的体系结构上实际工作。 float 中的内存是一组位,您解释这些位并使用 xor 操作完全交换它们。然后您可以再次使用这些位作为正确的 float
s。您引用的引述仅表示您交换的两个值需要属于同一类型,并且都是 int
s.
但是,在某些体系结构上,这可能会导致在不同类型的寄存器之间移动,或者显式地将寄存器刷新到内存中。如今,您将在几乎所有具有合理优化编译器的合理架构上看到的是,使用 std::swap
或带有临时变量的表达式的显式交换实际上更快。
即你应该写:
float a = 255.33333f;
float b = 0.123023f;
float tmp = a;
a = b;
b = tmp;
或者最好是:
float a = 255.33333f;
float b = 0.123023f;
std::swap(a,b);
如果您的体系结构的标准库作者确定异或交换确实有益,那么您应该希望最后一种形式会使用它。就在不必要的神秘实现中隐藏意图而言,异或交换是一个典型的坏习语。它仅在严重 register-starved 优化器不佳的情况下才有效。
您的代码调用了未定义的行为。在 C 或 C++ 中将 float*
转换为 int*
并照此使用是不合法的。 reinterpret_cast
应该用于在具有兼容布局的不相关结构之间进行转换,或者在类型指针和 void*
.
哦,在这种特殊情况下,UB 不仅仅是学术问题。编译器可能会注意到 xorSwap()
没有触及任何 float
s,执行语言别名规则允许的优化,并打印出 a
和 [= 的原始值17=] 而不是交换值。这甚至还没有进入 int
和 float
具有不同大小或对齐方式的架构。
如果您想安全地执行此操作,则必须 memcpy()
从浮点数到无符号字符数组,在循环中执行 XOR,然后 memcpy()
返回。这当然会使操作比正常交换慢。当然,xor-based 交换已经比正常交换慢了。
是:
a) 在编译器允许的情况下可能。
b) 标准没有定义行为的操作(即未定义的行为)
c) 在 gcc 上,实际上比准确说明你想要的更有效:
给定:
void xorSwap (unsigned int* x, unsigned int* y) {
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
void swapit3(float& a, float&b)
{
xorSwap(reinterpret_cast<unsigned int*>(&a), reinterpret_cast<unsigned int*>(&b));
}
结果:
swapit3(float&, float&): # @swapit3(float&, float&)
mov eax, dword ptr [rdi]
xor eax, dword ptr [rsi]
mov dword ptr [rdi], eax
xor eax, dword ptr [rsi]
mov dword ptr [rsi], eax
xor dword ptr [rdi], eax
ret
而这个:
void swapit2(float& a, float&b)
{
std::swap(a,b);
}
结果:
swapit2(float&, float&): # @swapit2(float&, float&)
mov eax, dword ptr [rdi]
mov ecx, dword ptr [rsi]
mov dword ptr [rdi], ecx
mov dword ptr [rsi], eax
ret