C - 在两个数字之间交换一点

时间:2017-07-27 21:36:23

标签: c bit-manipulation bitwise-operators bit

我刚试过这段代码:

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);

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

提前致谢

3 个答案:

答案 0 :(得分:5)

首先,您应该将数字中的相应位置设置为0,然后将其与实际位进行或运算,删除所有条件:

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

另一个号码也一样。

答案 1 :(得分:1)

形成面具

unsigned char mask = 1u << bitPosition;

然后用XOR swap algorithm赢得同伴群体的愤怒。

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

请注意,numbA == numbB时会失败。

答案 2 :(得分:1)

单个位并不比任意位掩码容易,所以我们来谈谈它。您始终可以使用1U << bitpos调用此函数。

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

XOR,1次翻转; XOR为0是无操作。

所以我们想要的是一个值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指令的分支几乎不值得。

不是xor-swap 。它确实使用临时存储。正如@ chux的答案所示,屏蔽的xor-swap需要3个AND操作以及3个XOR。 (并且通过要求&结果的临时寄存器或其他存储来抵消XOR交换的唯一好处。)

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

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