【问题标题】:Implementing Logical Right Shift in C在 C 中实现逻辑右移
【发布时间】:2017-01-23 07:31:05
【问题描述】:

我正在使用 C 语言中的逻辑右移函数,只使用位运算符。这是我所拥有的:

int logical_right_shift(int x, int n)
{
    int size = sizeof(int); // size of int

    // arithmetic shifts to create logical shift, return 1 for true
    return (x >> n) & ~(((x >> (size << 3) - 1) << (size << 3) -1)) >> (n-1);
}

这实际上适用于所有情况,但 n = 0 除外。我一直在试图找出一种修复它的方法,以便它也适用于 n = 0,但我被卡住了。

【问题讨论】:

  • 您正在为有符号类型创建逻辑右移运算符?
  • if (n == 0) { return x; } 有什么问题?
  • 使用@Mark 的建议,你可以把它改成这样(不保证它有效):return (n == 0 ? x : (x &gt;&gt; n) &amp; ~(((x &gt;&gt; (size &lt;&lt; 3) - 1) &lt;&lt; (size &lt;&lt; 3) -1)) &gt;&gt; (n-1));
  • @Dan:你想在答案中允许转换吗?
  • 它应该在没有强制转换或条件语句的情况下完成。

标签: c bit-manipulation


【解决方案1】:
int lsr(int x, int n)
{
  return (int)((unsigned int)x >> n);
}

【讨论】:

  • 这是正确的答案(以不必要和丑陋的结果为模)。负值的右移具有实现定义的行为(或者它是实现定义的值?我忘记了),因此就在有符号值上使用 &gt;&gt; 运算符而言,您的 lsr 函数的任何实现都很简单非便携式。
  • 此解决方案的一个问题:严格来说,在n==0 的情况下,它具有实现定义的行为,因为从int 转换为unsigned 并返回会导致实现定义的行为,如果原始值为负。第一次转换必须以 UINT_MAX+1 为模,但转换回有符号 int 可能只是对表示的重新解释,在这种情况下,值会改变。
  • @R.. 嗨,你能举个例子,或者一个 sn-p 代码,可以帮助我理解你的意思吗?谢谢。
【解决方案2】:

这是你需要的:

int logical_right_shift(int x, int n)
{
    int size = sizeof(int) * 8; // usually sizeof(int) is 4 bytes (32 bits)
    return (x >> n) & ~(((0x1 << size) >> n) << 1);
}

解释

x &gt;&gt; n 向右移动 n bits。但是,如果x 为负数,则符号位(最左边的位)将被复制到其右侧,例如:

假设这里的每个 int 都是 32 位,让
x     = -2147483648 (10000000 00000000 00000000 00000000),然后
x &gt;&gt; 1 = -1073741824 (11000000 00000000 00000000 00000000)
x &gt;&gt; 2 = -536870912  (11100000 00000000 00000000 00000000)
等等。

因此,当 n 为负数时,我们需要删除那些符号额外的符号位。

在这里假设n = 5

0x1 &lt;&lt; size1 移动到最左边的位置:

(10000000 00000000 00000000 00000000)

((0x1 &lt;&lt; size) &gt;&gt; n) &lt;&lt; 1 将 1 复制到其 n-1 邻居:

(11111000 00000000 00000000 00000000)

~((0x1 &lt;&lt; size) &gt;&gt; n) &lt;&lt; 1! 反转所有位:

(00000111 11111111 11111111 11111111)

所以我们最终获得了一个掩码来从x &gt;&gt; n 中提取真正需要的内容:

(x >> n) & ~(((0x1 << size) >> n) << 1)

&amp; 操作可以解决问题。

并且这个函数的总成本是6操作。

【讨论】:

  • 我认为这是正确的。但是你能做 32 次左移吗?那将是一个不确定的转变?
  • 是的,如果int 是,例如,4 个字节,这将失败。他需要从size 中减去 1,这样如果int 是 4 个字节,size 就是 31。
【解决方案3】:

只需将您的int 存储在unsigned int 中,然后对其执行&gt;&gt;

(如果使用 unsigned int,则符号不会扩展或保留)

http://en.wikipedia.org/wiki/Logical_shift

【讨论】:

    【解决方案4】:

    我认为问题出在您的“>> (n-1)” 部分。如果 n 为 0,则左侧部分将移动 -1。 所以,这是我的解决方案

    int logical_right_shift(int x, int n)
    {
      int mask = ~(-1 << n) << (32 - n);
      return  ~mask & ( (x >> n) | mask); 
    }
    

    【讨论】:

    • 这是一个优雅的解决方案,因为它不涉及条件语句,因此没有 CPU 分支。最后的| mask 操作是不必要的,因为通过 OR 操作设置为 1 的每个位都将通过 AND 操作设置为 0。掩码计算可以简化如下:int mask = int.MinValue &gt;&gt; (n - 1); 但如果 n == 0,我的简化将产生不正确的结果,因此这取决于您的用例 - 这种简化不适用于库。
    【解决方案5】:

    源自php's implementation of logical right shifting

    function logical_right_shift( i , shift ) {
    
        if( i & 2147483648 ) {
            return ( i >> shift ) ^ ( 2147483648 >> ( shift - 1 ) );
        }
    
        return i >> shift;
    }
    

    仅适用于 32 位平台。

    【讨论】:

      【解决方案6】:

      与@Ignacio 的评论一样,我不知道您为什么要这样做(而不是像其他答案那样只对unsigned 进行强制转换),但是呢(假设二进制补码和二进制)有符号的移位是算术的):

      (x >> n) + ((1 << (sizeof(int) * CHAR_BIT - n - 1)) << 1)
      

      或:

      (x >> n) ^ ((INT_MIN >> n) << 1)
      

      【讨论】:

        【解决方案7】:

        Milnex 的答案很棒,并且有一个很棒的解释,但不幸的是,由于总大小的变化,实施失败了。这是一个工作版本:

        int logicalShift(int x, int n) {
          int totalBitsMinusOne = (sizeof(int) * 8) - 1; // usually sizeof(int) is 4 bytes (32 bits)
          return (x >> n) & ~(((0x1 << totalBitsMinusOne) >> n) << 1);
        }
        

        要将 1 作为最高有效位,其他位置全为零,我们需要将 0x1 移动 number of bits - 1。我正在提交自己的答案,因为我对已接受答案的编辑以某种方式被拒绝了。

        【讨论】:

          【解决方案8】:
          int logicalShift(int x, int n) {
            int mask = x>>31<<31>>(n)<<1;
            return mask^(x>>n);
          }
          

          仅适用于 32 位

          【讨论】:

          • 请添加更多信息来解释您的答案。此处不接受纯代码答案。
          • @MarkYisri Ironic,因为在这个“线程”上投票最多的答案是纯代码的,并且完美、清晰地回答了这个问题。
          • 如果x &gt;&gt; 31 产生非零值,这会导致未定义的行为
          猜你喜欢
          • 1970-01-01
          • 2017-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-11-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多