可以通过使用位操作一次递增地移动多个位来执行此类操作。
32 位整数示例:
uint32_t reverse(uint32_t x, size_t len)
{
assert(len > 0 && len <= 4);
x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1);
x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2);
x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4);
x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8);
x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16);
return x >> (32 - len * 8);
}
这应该是最快且缓存友好的实现。
要为自定义宽度整数实现类似功能,您需要能够构造具有“X 位超过 X 位集”的位掩码,例如1 后 1 位(每隔一个位),2 后 2 等等。其余的应该是不言自明的。
您可以在此基础上实现其他变体。如果您需要在运行时选择它们,最好始终使用最宽版本(64 位)以避免分支。
注意:GCC 能够在 return 语句之前的最后两行中识别字节交换操作,并且能够在 x86 上生成bswap,在 ARM 上生成rev。使用 64 位 CPU,这使得 64 位版本在速度上与上述版本相当。
显然,如果不参考一些论文/书籍/专利,您现在无法发布基于常用技术的算法。因此,我在这里发布了一个高度专利、受版权保护并以其他方式受到保护的通用版本。 请注意,在使用它之前你应该三思而后行(因为它是专利)。
不幸的是,从这段代码生成的程序集完全是垃圾。只有 clang 能够以某种方式了解正在发生的事情并将其优化掉。
我几年前编写的原始实现是用 C++ 编写的,并且严重依赖模板来帮助编译器。最后,这是一个可怕而复杂的混乱,但可以为 8、16、32 和 64 整数生成许多面向位的算法(如果编译器支持它们,甚至更多)。尽管如此,这可以作为上述算法的描述。
此代码使用 64 位代码的单个实例为 8、16、32、64 位字实现反向算法。辅助函数 repeat、select 可以用作许多位算法的基础(尽管它们基本上会生成所需的位掩码)。
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
/**
* @brief Mask with `n` bits set.
*/
static uint64_t bits(size_t n)
{
return (((1ull << n) - 1) | (-uint64_t(n >= 64)));
}
static uint64_t do_repeat(uint64_t x, size_t w, size_t n)
{
if (n == 0)
return x;
const size_t shift = w * (n - 1);
return (x << shift) | do_repeat(x, w, n - 1);
}
/**
* @brief Repeat pattern over 64-bit word.
*
* @code
* assert(repeat( 0x1, 32) == 0x5555555555555555);
* assert(repeat( 0x3, 16) == 0x3333333333333333);
* assert(repeat( 0xF, 8) == 0x0F0F0F0F0F0F0F0F);
* assert(repeat( 0xFF, 4) == 0x00FF00FF00FF00FF);
* assert(repeat( 0xFFFF, 2) == 0x0000FFFF0000FFFF);
* assert(repeat(0xFFFFFFFF, 1) == 0x00000000FFFFFFFF);
* assert(repeat( 0x1, 16) == 0x1111111111111111);
* assert(repeat( 0x12, 8) == 0x1212121212121212);
* assert(repeat( 0x1234, 4) == 0x1234123412341234);
* assert(repeat(0x12345678, 2) == 0x1234567812345678);
* @endcode
*/
static uint64_t repeat(uint64_t x, size_t n)
{
assert(n != 0);
return do_repeat(x, 64 / n, n);
}
/**
* @brief Selects `1 << n` bits over `1 << n` bits.
*
* @code
* assert(select(0) == 0x5555555555555555);
* assert(select(1) == 0x3333333333333333);
* assert(select(2) == 0x0F0F0F0F0F0F0F0F);
* assert(select(3) == 0x00FF00FF00FF00FF);
* assert(select(4) == 0x0000FFFF0000FFFF);
* assert(select(5) == 0x00000000FFFFFFFF);
* @endcode
*/
static uint64_t select(size_t n)
{
assert(n < 6);
return repeat(bits(1 << n), 1 << (5 - n));
}
static uint64_t do_reverse(uint64_t x, size_t n)
{
const size_t shift = (1ull << n);
const uint64_t lo = select(n) << 0;
const uint64_t hi = select(n) << shift;
x = ((x & lo) << shift) | ((x & hi) >> shift);
if (n == 0)
return x;
return do_reverse(x, n - 1);
}
uint64_t reverse64(uint64_t x)
{
return do_reverse(x, 5);
}
uint32_t reverse32(uint32_t x)
{
return do_reverse(x, 4);
}
uint16_t reverse16(uint32_t x)
{
return do_reverse(x, 3);
}
uint8_t reverse8(uint8_t x)
{
return do_reverse(x, 2);
}
int main()
{
assert(repeat( 0x1, 32) == 0x5555555555555555);
assert(repeat( 0x3, 16) == 0x3333333333333333);
assert(repeat( 0xF, 8) == 0x0F0F0F0F0F0F0F0F);
assert(repeat( 0xFF, 4) == 0x00FF00FF00FF00FF);
assert(repeat( 0xFFFF, 2) == 0x0000FFFF0000FFFF);
assert(repeat(0xFFFFFFFF, 1) == 0x00000000FFFFFFFF);
assert(repeat( 0x1, 16) == 0x1111111111111111);
assert(repeat( 0x12, 8) == 0x1212121212121212);
assert(repeat( 0x1234, 4) == 0x1234123412341234);
assert(repeat(0x12345678, 2) == 0x1234567812345678);
assert(select(0) == 0x5555555555555555);
assert(select(1) == 0x3333333333333333);
assert(select(2) == 0x0F0F0F0F0F0F0F0F);
assert(select(3) == 0x00FF00FF00FF00FF);
assert(select(4) == 0x0000FFFF0000FFFF);
assert(select(5) == 0x00000000FFFFFFFF);
assert(reverse8 ( 0xA5) == 0xA5);
assert(reverse16( 0xFEA5) == 0xA57F);
assert(reverse32( 0xFE0000A5) == 0xA500007F);
assert(reverse64(0xFE00FE0000A500A5) == 0xA500A500007F007F);
return 0;
}