简体   繁体   中英

How to toggle a bit n times without using loop in C/C++?

I would like to toggle a bit n times without loop.
like 1(bit) after toggling 3 times will be 0 and so on. For toggling a bit 1 time I use bit^=1 . I actually searching a bit manipulation formula to do so.

If I understand correctly, you want to toggle a bit N times.

Now, Toggling a bin N times equals toggling N%2 times so:

b ^= (N%2);

Modulo 2 is the same as N&1 so you can also write:

b ^= (N&1);

generalised solution:

int toggle_bit_in_word(int word, int bit, int ntimes)
{
  auto archetype = ntimes & 1;
  auto toggler = archetype << bit;
  return word ^= toggler;
}

gcc 5.3 produces this code:

toggle_bit_in_word(int, int, int):
        and     edx, 1
        shlx    edx, edx, esi
        mov     eax, edx
        xor     eax, edi
        ret

for fun, let's write it the naiive way and enjoy the hilarity:

int toggle_bit_in_word_naiive(int word, int bit, int ntimes)
{
  auto toggler = 1 << bit;
  while (ntimes--)
    word ^= toggler;

  return word;
}

output (5.3):

toggle_bit_in_word_naiive(int, int, int):
        mov     ecx, 1
        mov     eax, edi
        shlx    esi, ecx, esi
        lea     edi, [rdx-1]
        test    edx, edx
        je      .L48
        lea     ecx, [rdx-8]
        shr     ecx, 3
        add     ecx, 1
        lea     r9d, [0+rcx*8]
        cmp     edi, 12
        jbe     .L4
        vmovd   xmm1, esi
        xor     r8d, r8d
        vpxor   xmm0, xmm0, xmm0
        vpbroadcastd    ymm1, xmm1
.L5:
        add     r8d, 1
        vpxor   ymm0, ymm0, ymm1
        cmp     ecx, r8d
        ja      .L5
        vpxor   xmm1, xmm1, xmm1
        vperm2i128      ymm2, ymm0, ymm1, 33
        vpxor   ymm0, ymm0, ymm2
        sub     edi, r9d
        vperm2i128      ymm2, ymm0, ymm1, 33
        vpalignr        ymm2, ymm2, ymm0, 8
        vpxor   ymm0, ymm0, ymm2
        vperm2i128      ymm1, ymm0, ymm1, 33
        vpalignr        ymm1, ymm1, ymm0, 4
        vpxor   ymm0, ymm0, ymm1
        vmovd   ecx, xmm0
        xor     eax, ecx
        cmp     edx, r9d
        je      .L47
        vzeroupper
.L4:
        xor     eax, esi
        test    edi, edi
        je      .L48
        xor     eax, esi
        cmp     edi, 1
        je      .L48
        xor     eax, esi
        cmp     edi, 2
        je      .L48
        xor     eax, esi
        cmp     edi, 3
        je      .L48
        xor     eax, esi
        cmp     edi, 4
        je      .L48
        xor     eax, esi
        cmp     edi, 5
        je      .L48
        xor     eax, esi
        cmp     edi, 6
        je      .L48
        xor     eax, esi
        cmp     edi, 7
        je      .L48
        xor     eax, esi
        cmp     edi, 8
        je      .L48
        xor     eax, esi
        cmp     edi, 9
        je      .L48
        xor     eax, esi
        cmp     edi, 10
        je      .L48
        xor     eax, esi
        xor     esi, eax
        cmp     edi, 11
        cmovne  eax, esi
        ret
.L47:
        vzeroupper
.L48:
        ret

8-/

of course, when an optimiser has all the information it needs, even naiive code becomes efficient:

int main(int argc, char**argv)
{
  return toggle_bit_in_word_naiive(argc, 3, 3);
}

result:

main:
        mov     eax, edi
        xor     eax, 8
        ret

This is impossible if you only ever manipulate bits independently.

That's because a particular bit does not know its previous two states.

You need a minimum of 2 bits if you want a cycle with a periodicity of 4.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM