位运算 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);
明确请求字节交换操作。
我正在从连接到覆盆子(手臂)的传感器读取 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);
明确请求字节交换操作。