【问题标题】:Understanding Left and Right Bitwise shift on a 128-bit number了解 128 位数字的左右按位移位
【发布时间】:2011-11-30 17:21:00
【问题描述】:

洋蓟101asked this:

假设我有一个由 4 个 32 位整数组成的数组,用于存储 128 位数字

如何对这个 128 位数字进行左移和右移?"

我的问题与 Remus Rusanu 给出的答案有关:

void shiftl128 (
    unsigned int& a,
    unsigned int& b,
    unsigned int& c,
    unsigned int& d,
    size_t k)
{
    assert (k <= 128);
    if (k > 32)
    {
        a=b;
        b=c;
        c=d;
        d=0;
        shiftl128(a,b,c,d,k-32);
    }
    else
    {
        a = (a << k) | (b >> (32-k));
        b = (b << k) | (c >> (32-k));
        c = (c << k) | (d >> (32-k));
        d = (d << k);
    }
}

void shiftr128 (
    unsigned int& a,
    unsigned int& b,
    unsigned int& c,
    unsigned int& d,
    size_t k)
{
    assert (k <= 128);
    if (k > 32)
    {
        d=c;
        c=b;
        b=a;
        a=0;
        shiftr128(a,b,c,d,k-32);
    }
    else
    {
        d = (c << (32-k)) | (d >> k); \
        c = (b << (32-k)) | (c >> k); \
        b = (a << (32-k)) | (b >> k); \
        a = (a >> k);
    }
}

让我们只关注一个班次,比如说左班次。具体来说,

a = (a << k) | (b >> (32-k));
b = (b << k) | (c >> (32-k));
c = (c << k) | (d >> (32-k));
d = (d << k);

如何左移 128 位数字?我知道什么是位移,

a = (a &lt;&lt; k) | (b &gt;&gt; (32-k)) 如何正确移动 128 位数字的第一部分 (32)?

【问题讨论】:

  • 我建议不要硬编码32,而是使用sizeof(unsigned int)。它应该是32,但至少你会确定它。
  • sizeof(unsigned int) 将是 4。:) 所以,你需要 sizeof(unsigned int)*CHAR_BIT

标签: c++ bit-manipulation


【解决方案1】:

这种技术有点惯用。让我们简化为ab。我们开始:

+----------+----------+
|    a     |    b     |
+----------+----------+

我们想左移一些来获得:

+----------+----------+
|  a    :  |  b    :  |  c  ...
+----------+----------+
|<--x-->|  |
      ->|y |<-

所以X 就是a &lt;&lt; kybk msbs,在单词中右对齐。您可以使用b &gt;&gt; (32-k) 获得该结果。

总的来说,你得到:

a = x | y
  = (a << k) | (b >> (32-k))

[注意:这种方式只对1 k

【讨论】:

  • 因此,上述代码中的守卫在 k 大于 32 时递归调用自身。这不是最快的方法,但它就是这样 ;)
  • @Michael Dorgan:它需要检查k &gt;= 32。而且它不处理 k == 0 的其他特殊情况。
  • 哇,这个可视化的例子很有帮助。非常感谢!
【解决方案2】:

a 的位向左移动时,必须有东西填充右端剩余的空间。由于ab 在概念上彼此相邻,因此通过移动a 的位而留下的空白将被移出b 末尾的位填充。表达式b &gt;&gt; (32-k) 计算从b 移出的位。

【讨论】:

  • 感谢您添加来源! :D
【解决方案3】:

您需要记住,在转移过程中“丢失”数据是可以接受的。

理解移位的最简单方法是想象一个窗口。例如,让我们处理字节。您可以将一个字节视为:

  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                 [      B        ]

现在,移动就是移动这个窗口:

  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
 [     B >> 8    ]
   [     B >> 7    ]
     [     B >> 6    ]
       [     B >> 5    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
         [     B >> 4    ]
           [     B >> 3    ]
             [     B >> 2    ]
               [     B >> 1    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                   [     B << 1    ]
                     [     B << 2    ]
                       [     B << 3    ]
                         [     B << 4    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0
                           [     B << 5    ]
                             [     B << 6    ]
                               [     B << 7    ]
                                 [     B << 8    ]
  0 0 0 0 0 0 0 0 a b c d e f g h 0 0 0 0 0 0 0 0

如果你看箭头的方向,你可以认为它有一个固定的窗口和一个移动的内容......就像你喜欢的手机触摸屏一样!

那么,a = (a &lt;&lt; k) | (b &gt;&gt; (32-k)) 表达式中发生了什么?

  • a &lt;&lt; k 选择a 的最右边的32 - k 位并将它们向左移动,在右侧创建k 0 的空间
  • b &gt;&gt; (32-k) 选择b 的最左边的k 位并将它们向右移动,在左侧创建32 - k 0 的空间
  • 两者合并在一起

回到使用字节长度的位:

  • 假设a[a7, a6, a5, a4, a3, a2, a1, a0]
  • 假设b[b7, b6, b5. b4, b3, b2, b1, b0]
  • 假设k3

代表的数字是:

// before
 a7 a6 a5 a4 a3 a2 a1 a0 b7 b6 b5 b4 b3 b2 b1 b0
[           a           ]
                        [           b           ]

// after (or so we would like)
 a7 a6 a5 a4 a3 a2 a1 a0 b7 b6 b5 b4 b3 b2 b1 b0
         [           a           ]
                                 [           b           ]

所以:

  • a &lt;&lt; 3 实际上变成了a4 a3 a2 a1 a0 0 0 0
  • b &gt;&gt; (8 - 3) 变为 0 0 0 0 0 b7 b6 b5
  • 结合|我们得到a4 a3 a2 a1 a0 b7 b6 b5

冲洗并重复 :)

【讨论】:

    【解决方案4】:

    请注意,在 else 情况下,k 保证为 32 或更少。因此,您的较大数字的每个部分实际上都可以移动 k 位。但是,将其向左或向右移动会使 k 高/低位为 0。要移动整个 128 位数字,您需要用相邻数字“移出”的位填充这些 k 位。

    在左移k的情况下,高数的低k位需要用低数的高k位填充。为了得到这些高 k 位,我们将(32 位)数字右移 32-k 位,现在我们将这些位放在正确的位置以从较高的数字中填充零 k 位。

    顺便说一句:上面的代码假定 unsigned int 正好是 32 位。那不是便携式的。

    【讨论】:

      【解决方案5】:

      为简化起见,考虑一个 16 位无符号 short,我们将高字节和低字节分别存储为 unsigned char h, l。 为了进一步简化,让我们将其左移一位,看看结果如何。

      我将其写为 16 个连续位,因为这就是我们要建模的内容:

      [h7 h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0]
      

      所以,[h, l] &lt;&lt; 1 将是

      [h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0 0]
      

      (高位,h7 已从高位旋转,低位用零填充)。 现在让我们将其分解为hl ...

      [h, l] = [h6 h5 h4 h3 h2 h1 h0 l7 l6 l5 l4 l3 l2 l1 l0 0]
      => h = [h6 h5 h4 h3 h2 h1 h0 l7]
           = (h << 1) | (l >> 7)
      

      等等

      【讨论】:

        【解决方案6】:

        我在 little endian 环境中对 128 位数字进行逻辑左移的变体:

        typedef struct { unsigned int component[4]; } vector4; 
        vector4 shift_left_logical_128bit_le(vector4 input,unsigned int numbits) {
            vector4 result;
            if(n>=128) {
                 result.component[0]=0;
                 result.component[1]=0;
                 result.component[2]=0;
                 result.component[3]=0;
                 return r;
            }
            result=input;
            while(numbits>32) {
                numbits-=32;
                result.component[0]=0;
                result.component[1]=result.component[0];
                result.component[2]=result.component[1];
                result.component[3]=result.component[2];
            }
            unsigned long long temp;
            result.component[3]<<=numbits;
            temp=(unsigned long long)result.component[2];
            temp=(temp<<numbits)>>32;
            result.component[3]|=(unsigned int)temp;
            result.component[2]<<=numbits;
            temp=(unsigned long long)result.component[1];
            temp=(temp<<numbits)>>32;
            result.component[2]|=(unsigned int)temp;
            result.component[1]<<=numbits;
            temp=(unsigned long long)result.component[0];
            temp=(temp<<numbits)>>32;
            result.component[1]|=(unsigned int)temp;
            result.component[0]<<=numbits;
            return result;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-28
          • 1970-01-01
          • 2014-12-30
          • 1970-01-01
          • 2017-01-07
          • 1970-01-01
          相关资源
          最近更新 更多