在 C++ 中交换 unsigned int 中两个最低位的最快方法

The fastest way to swap the two lowest bits in an unsigned int in C++

假设我有:

unsigned int x = 883621;

二进制是:

00000000000011010111101110100101

我需要交换两个最低位的最快方法:

00000000000011010111101110100110

注意:澄清一下:如果 x 是 7 (0b111),输出应该仍然是 7。

我会用

x = (x & ~0b11) | ((x & 0b10) >> 1) | ((x & 0b01) << 1);

如果你有几个字节的空闲内存,我会从查找开始 table:

constexpr unsigned int table[]={0b00,0b10,0b01,0b11};

unsigned int func(unsigned int x){
    auto y = (x & (~0b11)) |( table[x&0b11]);
    return y;  
}

Quickbench -O3 of all the answers so far.

Quickbench -Ofast of all the answers so far.

(加上我的 ifelse 天真的想法。)

[随时添加您自己并编辑我的答案]。

如果您认为基准测试不正确,请纠正我,我不是阅读汇编的专家。所以希望 volatile x 阻止在循环之间缓存结果。

我暂时忽略最高位 - 有一个使用乘法的技巧。乘法实际上是一种卷积运算,您可以用它来洗牌。

特别是,假设两个低位是AB。将其乘以 0b0101,得到 ABAB。您会看到交换的位 BA 是中间位。

因此, x = (x & ~3U) | ((((x&3)*5)>>1)&3)

[edit] 需要 &3 来去除顶部的 A 位,但是使用 std::uint_32_t 您可以使用溢出免费丢失该位 - 乘法然后得到结果 BAB0'0000'0000'0000 '0000'0000'0000'0000'0000' :

x = (x & ~3U) | ((((x&3)*0xA0000000)>>30));

另一个想法,避免剥离最高位。假设 x 的位为 XXXXAB,那么我们想用 0000(A^B)(A^B) 对它进行 x 或运算。于是

auto t = x^(x>>1); // Last bit is now A^B
t &=1; // take just that bit
t *= 3; // Put in the last two positions
x ^= t; // Change A to B and B to A. 

受到 table 想法的启发,但将 table 作为一个简单的常量而不是数组。我们只需要 mask(00)==00, mask(01)==11, mask(10)=11, masK(11)==11.

constexpr unsigned int table = 0b00111100;

unsigned int func(unsigned int x) {
    auto xormask = (table >> ((x&3) * 2)) &3;
    x ^= xormask;
    return x;  
}

这也使用了 dyungwang 的异或技巧来避免隔离最高位。

从数学的角度来看,我将从 rotate_left() 函数开始,它将位列表向左旋转一位(011 变为 110,然后 101,然后返回 011),并按如下方式使用:

int func(int input){
  return rotate_left(rotate_left((input / 4))) + rotate_left(input % 4);
}

在作者的例子中使用这个11010111101110100101:

input = 11010111101110100101;
input / 4 = 110101111011101001;
rotate_left(input / 4) = 1101011110111010010;
rotate_left(rotate_left(input / 4) = 11010111101110100100;

input % 4 = 01;
rotate_left(input % 4) = 10;

return 11010111101110100110;

还有一个shift()函数,可以用来(两次!)代替整数除法。