位运算 OR 与加法

Bit-operation OR vs addition

我正在从连接到覆盆子(手臂)的传感器读取 uint16。我通过以下方式将数据从小端转换为大端:

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

所以0A 0B之后就是0B 0A。

但我也看到有人用这个:

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

加法有什么好处吗?我的猜测是,没有真正的优势,只是慢了一点。

两个数字求和时有很大的不同,例如:

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

也许我已经回答了我自己的问题,但如果有人可以评价就更好了。我不是第一次自欺欺人了。

如果组合的位不重叠(换句话说,左侧的掩码对于由右侧,反之亦然)

此处显示的代码:

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

有点令人困惑 - 评论似乎暗示它是一个 32 位值,但计算暗示一个 16 位 result

你会得到不同的结果,例如,如果你这样做:

value |= 128;

value += 128;

value 已经设置了位 7。

请注意,至少在 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);
}

生成完全相同的代码:

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

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

使用按位或|可以清楚地表明谁阅读代码的意图是什么,这是重点。

我会对这两种方法之间的速度差异感到惊讶。

更好的是:

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

因为不需要屏蔽左边的部分(因为传入的位是零)。

如果是有符号整数而不是对负值执行右移操作时的结果取决于实现,因此可移植代码应使用

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

明确请求字节交换操作。