【问题标题】:C - Swap a bit between two numbersC - 在两个数字之间交换一点
【发布时间】:2017-07-27 21:36:22
【问题描述】:

我刚刚尝试了这段代码:

void swapBit(unsigned char* numbA, unsigned char* numbB, short bitPosition)//bitPosition 0-x
{
    unsigned char oneShift = 1 << bitPosition;

    unsigned char bitA = *numbA & oneShift;
    unsigned char bitB = *numbB & oneShift;

    if (bitA)
        *numbB |= bitA;
    else
        *numbB &= (~bitA ^ oneShift);

    if (bitB)
        *numbA |= bitB;
    else
        *numbA &= (~bitB ^ oneShift);
}

交换 a 和 b 的位位置 x 但由于 if() 我认为有更好的东西。

当我看到这个时:

*numbB &= (~bitA ^ oneShift);

我真的认为有一种更简单的方法可以做到这一点。 如果你有东西给我,我会接受的:)

提前致谢

【问题讨论】:

  • 这不是真正的“交换数字的两位”,更像是“交换两个数字之间的一位”之类的,不过这仍然是一个令人困惑的描述..
  • 第1步:使用无符号类型时,最好使用1u &lt;&lt; bitPosition(添加u
  • 谢谢,哈罗德,我修改了标题,我不介意...... Chux,我现在就用它,谢谢:)

标签: c bit-manipulation bitwise-operators bit


【解决方案1】:

首先你应该将一个数字中的对应位置设置为0,然后将它与实际位进行或,删除所有条件:

*numbB &= ~oneShift; // Set the bit to `0`
*numbB |= bitA;      // Set to the actual bit value

其他号码也一样。

【讨论】:

  • 非常好!非常感谢你
【解决方案2】:

单个位并不比任意位掩码容易,所以让我们谈谈这个。您可以随时使用1U &lt;&lt; bitpos 调用此函数。

如果两个值中的位位置相同,则两者都不需要更改。如果相反,它们都需要反转。

XOR 与 1 翻转一点;与 0 的 XOR 是空操作。

所以我们想要的是一个值,在输入之间存在位差异的任何地方都有一个1,而在其他任何地方都有一个 0。这正是a XOR b 所做的。只需将其屏蔽为仅交换一些位,我们就可以在 3 个 XOR + 1 AND 中进行位交换。

// call with  unsigned char mask = 1U << bitPosition; if you want
inline
void swapBit_char(unsigned char *A, unsigned char *B, unsigned char mask)
{
    unsigned char tmpA = *A, tmpB = *B; // read into locals in case A==B

    unsigned char bitdiff = tmpA ^ tmpB;
    bitdiff &= mask;               // only swap bits matching the mask
    *A = tmpA ^ bitdiff;
    *B = tmpB ^ bitdiff;
}

Godbolt compiler explorer with gcc for x86-64 and ARM,包括一个带有unsigned而不是unsigned char的版本。)

您可以考虑if(bitdiff) { ... },但除非您要通过避免分配来避免弄脏内存中的缓存行,否则可能不值得做任何条件行为。使用寄存器中的值(内联后),保存两条 xor 指令的分支几乎不值得。

不是异或交换。它确实使用临时存储。正如@chux 的回答所表明的那样,屏蔽的异或交换需要 3 次 AND 操作以及 3 次 XOR。 (并且通过需要临时寄存器或其他存储空间来存储 &amp; 结果,从而破坏了 XOR 交换的唯一好处。)

此版本只需要 1 个 AND。此外,最后两个 XOR 彼此独立,因此从输入到两个输出的总延迟仅为 3 次操作。 (通常为 3 个周期)。


对于 x86 asm 示例,请参阅此代码高尔夫 Exchange capitalization of two strings in 14 bytes of x86-64 machine code(带有注释的 asm 源)

【讨论】:

  • 弱编译器的微优化可以使用bitdiff = (*A ^ *B) &amp; mask; *A ^= bitdiff; *B ^= bitdiff;,这就像“此版本仅需要...”段落的结果。
  • @chux:在不知道A != B 的情况下,在执行*B ^= bitdiff 之前写入*A 在独立函数中是不等效的。检查与_mayalias 版本不同的代码生成的godbolt 链接(之所以命名是因为gcc 在写入*A 后必须重新加载*B。)
【解决方案3】:

形成面具

unsigned char mask = 1u << bitPosition;

然后通过XOR swap algorithm 赢得同行的愤怒。

*numbA ^= *numbB & mask;
*numbB ^= *numbA & mask;
*numbA ^= *numbB & mask;

请注意,当numbA == numbB 时,此操作会失败。

【讨论】:

  • 我想把它作为替代方案,但想起了 SO 同行对最近一个问题的愤怒☺️
  • @EugeneSh。 IMO,numbA == numbB 问题最终拒绝了这种方法,因为它是一个令人惊讶的错误。
  • @EugeneSh.:*numbB &amp; mask 已经需要在某个地方进行临时存储,因此它可以作为 XOR 的操作数。 (除非您使用的是具有三元布尔值的机器,例如 x86 AVX512F vpternlogd。)因此,掩码 xor-swap 破坏了 xor-swap 的唯一优势(在 asm 级别):没有额外的寄存器。我用 3 XOR 和只有 1 AND 发布了一个答案。 (我认为位差技术是众所周知的;我以前见过它,但以前没有完全理解它。)无论如何,它比 OP 的分支版本好。
猜你喜欢
  • 2015-10-18
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 2014-11-18
  • 1970-01-01
  • 2019-04-17
相关资源
最近更新 更多