【问题标题】:How to shift 32 bit int by 32 (yet again)如何将 32 位 int 移动 32(再次)
【发布时间】:2016-01-31 13:47:55
【问题描述】:

好的,所以我知道通常左移和右移仅适用于值0..31。我在考虑如何最好地扩展它以包括 32,这简化了一些算法。我想出了:

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

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

【问题讨论】:

  • 您是否考虑过移动 32 位,然后移动剩余的位置?
  • 实际上确实包含32个。只有32个以上没有定义。
  • Java 规范提到如果左手操作数是 32 位,它只使用右手操作数的低 5 位,我相信 ANSI C 也是如此。
  • 预期的结果是什么?一旦您更正了运算符优先级,所有这些都将为零。

标签: java c++ c bit-manipulation bit-shift


【解决方案1】:

在 Java 中,如果这些变量的类型为 int,则保证可以工作,因为 Java 中的 &gt;&gt; 执行算术右移,并且移位超过 31 也具有定义的行为。但要注意运算符优先级

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

这适用于班次计数最多 32 个。但它可以修改为包含移位计数大于 31 的任何 int 值返回 0

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

但是在 C 和 C++ 中,int 类型的大小和 &gt;&gt; 运算符的行为是 implementation defined。大多数(如果不是全部)modern 实现将其实现为带符号类型的算术移位。此外,移动超过可变​​宽度的行为是undefined。更糟糕的是,有符号溢出会调用 UB,因此即使左移 31 也是 UB (until C++14)。因此,要获得明确定义的输出,您需要

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

结果是

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 位类型然后进行掩码来简化此操作。 ARM 等一些 32 位平台确实支持 32 位移位,所以这种方法也很有效

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-18
    • 2013-05-03
    • 2015-08-03
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    相关资源
    最近更新 更多