哪种方式更好地获得64位整数中较低的32位

Which way is better to get lower 32 bits of a 64 bits integer

本文关键字:整数 32位 64位 方式 更好      更新时间:2023-10-16

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

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

但我测试了一下,发现以下更快:

lower = some_var & 0xffffffff;

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

&掩蔽更好:

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

E1>>E2的值是E1右移E2位的位置。[…]如果E1具有带符号的类型和负值,则生成的值是实现定义的。

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

掩蔽的一个缺点是,很容易意外地出现7或9个F错误,而32中的拼写错误是显而易见的:不过,还有其他方法可以生成掩蔽值,例如(1LL<<32)-1,或者时髦但优雅的uint32_t(-1)

当然,如果loweruint32_tsome_varuint64_t,您可以让转换是隐式的,所以优化器甚至不需要意识到按位and可以在赋值前删除,但这可能会给您一个编译器警告,您可以使其静音。…

uint32_t lower = static_cast<uint32_t>(some_var);

当分配给另一个uint64_t时,或者当掩码不是针对所有32个最低有效位时,掩码主要是有用的。

使用AND的掩码更好,因为它不依赖于值的有符号性。

但取低32位的最有效方法是将其分配给32位变量。

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

与位"与"的区别在于,CPU只占用较低的部分,而不进行任何逻辑运算。

如果你有一个32位的CPU,它只会忽略存储在第二个寄存器或内存位置的上限值。

如果您有一个64位CPU,它有一条将32位值扩展(无符号)为64位值的指令。

一个好的优化器会在这两种情况下生成相同的代码。对我来说,这是最直接的方法:lower = some_var & 0xffffffff;其他形式可能会产生不必要的偏移。

有时,当我想绝对确保编译器不会把事情搞砸时,我会使用并集来重叠变量。

例如:

typedef union {
    int64 QWORD;
    int32 DWORD[2];
} overlapper64;
overlapper someVariable;

然后访问它作为:

someVariable.QWORD;
int32 myVar32 = someVariable.DWORD[0];

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

根据其他人的说法,使用第二个选项可能会更快,因为如果不优化,第一个选项将实现为2条cpu指令,而第二个选择是一条cpu指令。这可能是您观察到使用第二个选项可以提高性能的原因。

我认为两者都一样好。然而,使用逐位运算符将是一种更好的方法(不确定是否存在任何性能差异),正如标准所说:

6.5.7位移位运算符

4 E1<lt;E2为E1左移位的E2位位置;腾空的位用零填充。如果E1无符号类型结果是E1×2E2,降模1大于最大值可在结果类型中表示。如果E1具有带符号类型且为非负值,并且E1×2E2是可表示的在结果类型中,则为结果值;否则行为未定义。