Swapping bits at a given point between two bytes

帅比萌擦擦* 提交于 2021-02-13 17:04:12

问题


Let's say I have these two numbers:

x = 0xB7
y = 0xD9

Their binary representations are:

x = 1011 0111
y = 1101 1001

Now I want to crossover (GA) at a given point, say from position 4 onwards.

The expected result should be:

x = 1011 1001
y = 1101 0111

Bitwise, how can I achieve this?


回答1:


I would just use bitwise operators:

t = (x & 0x0f)
x = (x & 0xf0) | (y & 0x0f)
y = (y & 0xf0) | t

That would work for that specific case. In order to make it more adaptable, I'd put it in a function, something like:

def swapBits (x, y, s, e):
    lookup = [255,127,63,31,15,7,3,1]
    mask = lookup[s] & !lookup[e]
    t = x & mask
    x = (x & !mask) | (y & mask)
    y = (y & !mask) | t
    return (x,y)

The lookup values allow you to specify which bits to swap. Let's take the values xxxxxxxx for x and yyyyyyyy for y along with start bit s of 2 and end bit e of 6 (bit numbers start at zero on the left in this scenario).

x        y        s e t        mask     !mask    execute
-------- -------- - - -------- -------- -------- -------
xxxxxxxx yyyyyyyy 2 6                   starting point
                              00111111  mask = lookup[2](00111111)
                              00111100       & !lookup[6](11111100)
                      00xxxx00          t = x & mask
xx0000xx                                x = x & !mask(11000011)
xxyyyyxx                                  | y & mask(00111100)
         yy0000yy                       y = y & !mask(11000011)
         yyxxxxyy                         | t(00xxxx00)



回答2:


If a bit position is the same in both values, no change is needed in either. If it's opposite, they both need to invert.

XOR with 1 flips a bit; XOR with 0 is a no-op.

So what we want is a value that has a 1 everywhere there's a bit-difference between the inputs, and a 0 everywhere else. That's exactly what a XOR b does.

Simply mask this bit-difference to only keep the differences in the bits we want to swap, and we have a bit-swap in 3 XORs + 1 AND.

Your mask is (1UL << position) -1. One less than a power of 2 has all the bits below that set. Or more generally with a high and low position for your bit-range: (1UL << highpos) - (1UL << lowpos). Whether a lookup-table is faster than bit-set / sub depends on the compiler and hardware. (See @PaxDiablo's answer for the LUT suggestion).

// Portable C:

//static inline
void swapBits_char(unsigned char *A, unsigned char *B)
{
    const unsigned highpos = 4, lowpos=0;  // function args if you like
    const unsigned char mask = (1UL << highpos) - (1UL << lowpos);

    unsigned char tmpA = *A, tmpB = *B; // read into locals in case A==B

    unsigned char bitdiff = tmpA ^ tmpB;
    bitdiff &= mask;               // clear all but the selected bits
    *A = tmpA ^ bitdiff;           // flip bits that differed
    *B = tmpB ^ bitdiff;
}

//static inline
void swapBit_uint(unsigned *A, unsigned *B, unsigned mask)
{
    unsigned tmpA = *A, tmpB = *B;

    unsigned bitdiff = tmpA ^ tmpB;
    bitdiff &= mask;               // clear all but the selected bits
    *A = tmpA ^ bitdiff;
    *B = tmpB ^ bitdiff;
}

(Godbolt compiler explorer with gcc for x86-64 and ARM)

This is not an xor-swap. It does use temporary storage. As @chux's answer on a near-duplicate question demonstrates, a masked xor-swap requires 3 AND operations as well as 3 XOR. (And defeats the only benefit of XOR-swap by requiring a temporary register or other storage for the & results.) This answer is a modified copy of my answer on that other question.

This version only requires 1 AND. Also, the last two XORs are independent of each other, so total latency from inputs to both outputs is only 3 operations. (Typically 3 cycles).


For an x86 asm example of this, see this code-golf Exchange capitalization of two strings in 14 bytes of x86-64 machine code (with commented asm source)




回答3:


Swapping individual bits with XOR

unsigned int i, j; // positions of bit sequences to swap
unsigned int n;    // number of consecutive bits in each sequence
unsigned int b;    // bits to swap reside in b
unsigned int r;    // bit-swapped result goes here

unsigned int x = ((b >> i) ^ (b >> j)) & ((1U << n) - 1); // XOR temporary
r = b ^ ((x << i) | (x << j));


来源:https://stackoverflow.com/questions/3562347/swapping-bits-at-a-given-point-between-two-bytes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!