Bit-operation OR vs addition

2019-09-18 06:23发布

问题:

I am reading an uint16 from a sensor connected to an raspberry (arm). I convert the data from little endian to big endian via:

// result = 0A 0B
// 0B 00 | 00 0A
(result << 8) | (result >> 8);

So 0A 0B is 0B 0A afterwards.

But I also saw people using this:

(result << 8) + (result >> 8);

Is there any advantage of using the addition? My guess is, there is no really advantage, it is just a bit slower.

There is a big difference when it comes to sum two numbers for example:

EF10 = 0FF0 + 00FF != 0FF0 | 00FF = 0FFF

Maybe I answered my own question already but it would be nice, if someone could evaluate. Would not be the first time I am tricking myself.

回答1:

There is no difference (or advantage, at least not on modern processors) if the bits being combined aren't overlapping (in other words, the mask on the left-hand side has zeros for all bits set by the right-hand side, and vice versa)

The code shown here:

// result = 0A0B0C0D 0E0F0A0B
// 0E0F0A0B 00000000 | 00000000 0A0B0C0D
((result << 8) & 0xFF00) | (result >> 8);

is a little confusing - the comment seems to imply that it's a 32-bit value, but the calculation implies a 16-bit result

You will get different results for example if you do something like this:

value |= 128;

vs

value += 128;

when value already has the bit 7 set.

Note that at least in clang:

#include <cstdint>

uint32_t func1(uint32_t x)
{
    return (x >> 8) | ((x << 8) & 0xFF000000);
}

uint32_t func2(uint32_t x)
{
    return (x >> 8) + ((x << 8) & 0xFF000000);
}

The exact same code is generated:

_Z5func1j:                              # @_Z5func1j
    movl    %edi, %eax
    shrl    $8, %eax
    shll    $8, %edi
    andl    $-16777216, %edi        # imm = 0xFFFFFFFFFF000000
    leal    (%rdi,%rax), %eax
    retq

_Z5func2j:                              # @_Z5func2j
    movl    %edi, %eax
    shrl    $8, %eax
    shll    $8, %edi
    andl    $-16777216, %edi        # imm = 0xFFFFFFFFFF000000
    leal    (%rdi,%rax), %eax
    retq


回答2:

Using bitwise or | makes it clear what is the intention to who reads the code, and that's the important point.

I would be surprised of speed differences between the two approaches.

Even better would be:

x = (x << 8) | (x >> 8);

because there is no need mask the left part (as incoming bits are zeros).

In case of a signed integer instead what is coming in when performing a right-shift operation on negative values is implementation dependent, therefore portable code should use

x = (x << 8) | ((x >> 8) & 0xFF);

to make it clear that a byte-swap operation is requested.