例如,我有一个uint8_t,可以是任何值,我只想把所有的位从最低有效位翻转到最高有效位的最后1位值?我该如何以最有效的方式做到这一点?,有没有一种解决方案可以避免使用循环?

以下是一些 case :

左侧是原始位,右侧是翻转后的位.

  • 0001100000010->00000010
  • 00000000->00000000
  • 11111111->00000000
  • 1110000100011->00001000
  • 01000000->00111111

[EDIT]

类型也可能大于uint8_t,可能是uint32_tuint64_t__uint128_t.我只使用uint8_t,因为它是示例中最容易显示的尺寸.

推荐答案

总的来说,我预计大多数解决方案大致有以下形式:

  1. 计算需要翻转的位的掩码
  2. 戴着面具的XOR

如 comments 中所述,x64是一个令人感兴趣的目标,在x64上,您可以执行如下步骤1:

  • 通过前导零(_lzcnt_u64)并从64(或32)中减go 该零(或32,以适当者为准),找到最有效1中以1为基础的位置p.
  • 创建一个包含p个连续设置位的掩码,从最低有效位开始,可能使用_bzhi_u64.

也有一些变化,比如使用BitScanReverse查找最重要的1(但它的大小写为0),或者使用移位而不是bzhi(但它的大小写为64).lzcntbzhi是一个很好的组合,没有难看的 case .bzhi需要BMI2(英特尔Haswell或更新版本、AMD Zen或更新版本).

综合起来:

x ^ _bzhi_u64(~(uint64_t)0, 64 - _lzcnt_u64(x))

第1步也可以使用几个移位和按位OR以通用方式实现(无特殊操作),如下所示:

m = x | (x >> 1);
m |= m >> 2;
m |= m >> 4;
m |= m >> 8;
m |= m >> 16;
m |= m >> 32;  // last step should be removed if x is 32-bit

C++相关问答推荐

当打印字符串时,为什么在c中没有使用常量限定符时我会收到警告?

Malloc(sizeof(char[Length]))是否不正确?

为什么该函数不将参数值保存到数据 struct 中?

在我的代码中,我需要在哪里编写输出函数?

Make Node函数.S有什么问题吗?

使用错误的命令执行程序

在句子中转换单词的问题

等同于铁 rust 的纯C语言S未实现!()宏

使用nmake for程序比Hello World稍微复杂一些

从不兼容的指针类型返回&&警告,但我看不出原因

如何对现有的双向循环链表进行排序?

某些EAX值的不同调用方的CPUID结果不一致

强制GCC始终加载常量(即只读),即使启用了优化

Leet代码运行时错误:代码不会在Leet代码上编译,而是在其他编译器中编译,如netbeans和在线编译器

将char*铸造为空**

一元运算符

malloc:损坏的顶部大小无法找出问题

在 C23 之前如何对空指针使用nullptr?

为什么这里的符号没有解析?

在 printf() 格式说明符中使用字段宽度变量