这个问题:
How do you set, clear, and toggle a single bit?
讨论了对较大值内的特定位的三种操作,它们以直接的方式对应于该位上的 OR 1、AND 0 和 XOR 1。但是如果我们事先不知道第二位的值怎么办?如果我们想要执行赋值,而我们在编译时不知道其他操作数位怎么办?也就是说,我们想要将位 block 中某个位置的一位设置为运行时提供的新的不相关位的值?
我希望在 Intel x86_64 上尽可能快地实现这一点(并忽略矢量化,我希望这与此无关)。另外,为简单起见,假设位 block 类型为 uint32_t
。
编辑:我的答案是 C 而不是 C++,因为这个问题实际上与 C++ 无关。
最佳答案
对于 32 位 block 类型的情况,这里有两种可能的实现:
#include <cstdint>
uint32_t bit_assign_v1(uint32_t block, uint8_t bit_index, bool x)
{
uint32_t mask = uint32_t { 1 } << bit_index;
return (block & ~mask) | (((uint32_t) x) << bit_index);
}
uint32_t bit_assign_v2(uint32_t block, uint8_t bit_index, bool x)
{
uint32_t mask = uint32_t { 1 } << bit_index;
return x ? (block & ~mask) : (block | mask);
}
使用 GodBolt,我为这两个选项获得了不同优化的代码,随着我们更改平台和编译器,这些代码也会有所不同。这是 example对于 Skylake(或者更好的是,查看 this version ,它是相同的代码,但分为更多的 C 语句,以便您可以更好地将程序集与 C 代码关联起来)。
GCC 8.2 组件:
bit_assign_1:
movzx eax, sil
btr edi, eax
movzx edx, dl
shlx eax, edx, eax
or eax, edi
ret
bit_assign_2:
mov ecx, 1
shlx esi, ecx, esi
andn eax, esi, edi
or esi, edi
test dl, dl
cmove eax, esi
ret
clang 7.0 组件:
bit_assign_1: # @bit_assign_1
btr edi, esi
shlx eax, edx, esi
or eax, edi
ret
bit_assign_2: # @bit_assign_2
mov eax, edi
btr eax, esi
bts edi, esi
test edx, edx
cmovne edi, eax
mov eax, edi
ret
我还没有对这些进行任何基准测试。
关于assembly - 分配给具有特定索引的位的好方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52632484/