Вот две возможные реализации для случая 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 (или, что еще лучше, посмотрите на эту версию , которая представляет собой тот же код, но разбит на несколько операторов 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
Я еще не тестировал ни один из этих вариантов.