这个问题:
How do you set, clear, and toggle a single bit?
在较大值内的特定位上讨论三个操作,这些操作直接对应于该位上的OR 1,AND 0和XOR 1。但是,如果我们事先不知道第二位的值怎么办?如果我们要执行赋值,而又在编译时不知道其他操作数位,该怎么办?也就是说,我们想要在运行时提供的一个新的无关位的值上,在某个位置将一个位块设置为一位吗?
例如,我想在Intel x86_64上实现最快的实现(并且忽略向量化,我希望这里无关紧要)。另外,为简单起见,假设位块类型为uint32_t
。
编辑:我的回答是C而不是C ++,因为这个问题确实没有C ++的含义。
答案 0 :(得分:2)
对于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
我还没有基准测试。