如何将 32 位 int 移动 32(再次)

How to shift 32 bit int by 32 (yet again)

好的,所以我知道通常左移和右移仅针对值 0..31 进行了明确定义。我在考虑如何最好地将它扩展到包括 32,从而简化一些算法。我想到了:

 int32 << n & (n-32) >> 5

这似乎行得通。问题是,它是否保证可以在任何架构(C、C++、Java)上工作,并且可以更有效地完成它吗?

在Java中,如果这些变量的类型是int,它保证可以工作,因为>>在Java中进行算术右移并且移动超过31也有定义的行为。但要注意运算符优先级

int lshift(int x, int n)
{
    return (x << n) & ((n-32) >> 5);
}

这适用于班次计数 最多 32。但它可以修改为包括任何移位计数大于 31 return 0

的 int 值
return (x << n) & ((n-32) >> 31);

但是在 C 和 C++ 中,int 类型的大小和 >> 运算符的行为是 implementation defined. Most (if not all) modern implementations implement it as arithmetic shift for signed types though. Besides, the behavior of shifting more than the variable width is undefined. Worse yet, signed overflow invokes UB so even a left shift by 31 is also UB (until C++14)。因此,要获得明确定义的输出,您需要

  • 使用像 uint32_t 这样的无符号固定宽度类型(所以 x << 31 不是 UB)
  • 使用为 >> 发出算术右移指令并为 n 使用有符号类型的编译器,或者自己实现算术移位
  • 屏蔽移位量以将其限制为 5 位 int32_t

结果会是

uint32_t lshift(uint32_t x, int32_t n)
{
    return (x << (n & 0x1F)) & ((n-32) >> 31);
}

如果体系结构支持 conditional instructions,如 x86 或 ARM,那么以下方式可能会更快

return n < 32 ? x << n : 0;

在 64 位平台上,您可以通过移入 64 位类型然后掩码来使这更简单。一些 32 位平台如 ARM 确实支持 32 位移位,所以这种方法也很有效

return ((uint64_t)x << (n & 0x3F)) & 0xFFFFFFFFU;

您可以看到输出程序集here。我看不出如何进一步改进它