如何在C/ c++中切换位n次而不使用循环

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

本文关键字:循环 换位 c++      更新时间:2023-10-16

我想切换位n次,不循环。
如1(位)切换3次后将为0,以此类推。对于切换位1,我使用bit^=1。我实际上搜索了一些操作公式来做到这一点。

如果我理解正确的话,您想要切换位N次。

现在,切换一个bin N次等于切换N%2次,所以:

b ^= (N%2);

模2与N&1相同,所以你也可以写:

b ^= (N&1);

一般解:

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生成以下代码:

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

为了好玩,让我们用朴素的方式来写,享受欢乐:

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

输出(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 -/

当然,当优化器拥有它需要的所有信息时,即使是原始代码也会变得高效:

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

main:
        mov     eax, edi
        xor     eax, 8
        ret

如果您只单独操作位,这是不可能的。

这是因为一个特定的位不知道它之前的两个状态。

如果你想要一个周期为4的周期,你需要最少2位