这个问题:
How do you set, clear, and toggle a single bit?
讨论了在较大值内的特定位上的三种操作,它们以直接方式对应于该位上的OR 1,AND 0和XOR 1。但是,如果我们事先不知道第二位的值怎么办?如果我们要执行赋值,而又在编译时不知道其他操作数位,该怎么办?也就是说,我们要在运行时提供的一个新的不相关位的值上,在某个位置的某个位块处设置一个位吗?
我想在Intel x86_64上实现最快的实现(并且忽略向量化,我希望这里无关紧要)。另外,为简单起见,假设位块类型为uint32_t
。
编辑:我的答案是C而不是C ++,因为关于这个问题的确没有C ++的意思。
最佳答案
对于32位块类型,这是两种可能的实现:
#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,我为这两个选项中的每个选项获得了不同的优化代码,随着我们更改平台和编译器的不同,代码也有所不同。这是Skylake的example(或者更好的是,请看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/