获取64位整数的低32位,哪种方法更好?

12

我发现在一些答案中,他们建议使用

lower = (some_var << 32) >> 32;

但是我测试后发现以下代码更快:

lower = some_var & 0xffffffff;

那么哪个更好?前者在某些情况下是否更安全或编译器优化后更快?


some_var 的确切类型是什么?具体来说,它是无符号的吗? - Suma
@Suma 是的,uint64_t。 - MoorLi
5个回答

7

使用 & 进行掩码更好:

  • & 对于有符号和无符号的 some_var 都是可靠的,而将负数右移位会产生一个实现定义的结果:

E1 >> E2 的值为 E1 向右移动 E2 位。[...] 如果 E1 具有有符号类型并且具有负值,则产生的值为实现定义。

  • 在我所知道的每个 CPU 上(Z80、6502C、x86、68000、UltraSparc),按位与都是单个 CPU 指令,并且只需要一个时钟周期... 它极不可能比你提到的位移方法慢或占用更多的机器代码字节,尽管编译器可能会优化为按位与。

掩码的一个缺点是相对容易意外地拥有 7 或 9 个 F,而在 32 中的打字错误则很明显:还有其他方法可以生成掩码值,例如 (1LL<<32)-1,或者 hackish 但某种程度上优雅的 uint32_t(-1)

当然,如果 loweruint32_t,而 some_varuint64_t,你可以让转换隐式进行,这样优化器甚至不需要意识到按位与在赋值之前就可以被删除,但这可能会给你一个编译器警告,你可以通过以下方式消除它...

uint32_t lower = static_cast<uint32_t>(some_var);

掩码主要在将值分配给另一个uint64_t时有用,或者当掩码不是应用于32个最低有效位时。

谢谢你!这真的非常详细和有帮助! - MoorLi

5

使用AND进行掩码处理更好,因为它不依赖于值的符号。

但是获取低32位最有效的方法是将其赋值给一个32位变量。

uint64_t u = 0x1122334455667788;
uint32_t n;

n = static_cast<uint32_t>(u);  // 0x55667788

与按位与的区别在于CPU只取下部分而不进行任何逻辑操作。如果您有一个32位的CPU,它会忽略存储在第二个寄存器或内存位置中的上限值。如果您有一个64位的CPU,则有一个单一指令将32位值扩展(无符号)为64位值。

1
一个好的优化器会在两种情况下生成相同的代码。对我来说,这是最直接的方法:lower = some_var & 0xffffffff; 另一种形式可能会产生不必要的移位。
有时我使用联合来重叠变量,当我想要绝对确保编译器不会搞砸事情时。
例如:
typedef union {
    int64 QWORD;
    int32 DWORD[2];
} overlapper64;

overlapper someVariable;

然后按如下方式访问它:
someVariable.QWORD;

int32 myVar32 = someVariable.DWORD[0];

根据平台/编译器,重叠发生的顺序可能会有所不同。 一定要在您特定的平台上进行测试。 在C中,我使用一堆特定于平台的#ifdef来自动控制顺序。


1

除了其他人说的,根据你使用的编译器,第二个选项可能更快,因为如果没有进行优化,第一个选项将被实现为2个CPU指令,而第二个选项只有1个CPU指令。这可能是你观察到使用第二个选项提高性能的原因。


0

我认为两种方法都很好。然而,使用位运算符会是一种更好的(不确定是否有性能差异)方法,因为标准规定:

6.5.7 位移操作符

4 E1 << E2 的结果是将 E1 左移 E2 位;空出的位用零填充。如果 E1 具有无符号类型,则结果的值为 E1 × 2E2,对结果类型可表示的最大值加一取模后的余数。如果 E1 具有带符号类型和非负值,并且 E1 × 2E2 可以在结果类型中表示,则该值就是结果;否则,行为未定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接