Какой хороший способ назначить бит с определенным индексом? - PullRequest
0 голосов
/ 03 октября 2018

Этот вопрос:

Как установить, очистить и переключить один бит?

обсуждает три операции над конкретным битом в пределах большего значения, котороепрямо соответствуют OR 1, AND 0 и XOR 1 в этом бите.Но что, если мы не знаем заранее значение этого второго бита?Что если мы хотим выполнить присваивание , где мы не знаем другого бита операнда во время компиляции?То есть мы хотим установить один бит в определенной позиции в битовом блоке в значение нового несвязанного бита, предоставляемого во время выполнения?

Я хотел бы иметь самую быструю реализацию для этого наскажем, Intel x86_64 (и игнорируя векторизацию, которая, я надеюсь, здесь неактуальна).Кроме того, предположим для простоты тип битового блока uint32_t.

Редактировать: Сделал мой ответ C, а не C ++, поскольку в этом вопросе на самом деле ничего нет в C ++.

1 Ответ

0 голосов
/ 03 октября 2018

Вот две возможные реализации для случая 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

Я еще не тестировал ни один из этих вариантов.

...