簡體   English   中英

了解128位數字的左右按位移位

[英]Understanding Left and Right Bitwise shift on a 128-bit number

朝鮮薊101 問這個

假設我有一個由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位數? 我理解什么位移位,<<移位位左,(8位數)像00011000左移2是01100000.同樣適用於右移,但是在右邊。 然后是單個“管道”| 是OR意味着任何32位數字中的任何1都將出現在結果中。

怎么是a = (a << k) | (b >> (32-k)) a = (a << k) | (b >> (32-k))移動128位數的第一部分(32)?

這種技術有些慣用。 讓我們簡化為ab 我們從:

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

我們想向左移一些金額來獲得:

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

所以X只是a << k ybk msbs,在單詞中右對齊。 你用b >> (32-k)得到那個結果。

總的來說,你得到:

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

[注意:此方法僅對1 <= k <= 31有效,因此您的代碼實際上是錯誤的。]

當位a GET向左側偏移,東西已經填寫右端留下的空間。 由於ab是概念性地彼此相鄰 ,通過移位的比特留下的空隙a由移位關閉的端部的位被填充b 表達式b >> (32-k)計算從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        ]

現在,轉移只是移動這個窗口:

  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 << k) | (b >> (32-k))發生了什么 a = (a << k) | (b >> (32-k))

  • a << k選擇32 - k的最右邊的位a ,並將其向左移動,產生的空間k右側0
  • b >> (32-k)選擇k最左邊的比特b ,並將它們移向右側,創建的空間32 - k左側0
  • 兩者合並在一起

回到使用字節長度的叮咬:

  • 假設a[a7, a6, a5, a4, a3, a2, a1, a0]
  • 假設b[b7, b6, b5. b4, b3, b2, b1, b0] [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 << 3確實變為a4 a3 a2 a1 a0 0 0 0
  • b >> (8 - 3)變為0 0 0 0 0 b7 b6 b5
  • |結合 我們得到a4 a3 a2 a1 a0 b7 b6 b5

沖洗並重復:)

我的變體為小端環境中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;
}

請注意,在else的情況下,k保證為32或更小。 因此,較大數字的每個部分實際上可以移位k位。 但是,向左或向右移動會使k高/低位為0.要移動整個128位數,您需要用相鄰數字“移出”的位填充這些k位。

在左移k的情況下,較高數的k個較低位需要用較低數的k個較高位填充。 為了獲得這些高k位,我們將該(32位)數字右移32-k位,現在我們將這些位置於正確的位置以從較高的數字填充零k位。

BTW:上面的代碼假設unsigned int正好是32位。 那不便攜。

為簡化起見,考慮一個16位無符號短路,其中我們將高字節和低字節分別存儲為unsigned char h, l 為了進一步簡化,我們只需將其向左移一位,看看情況如何。

我把它寫成16個連續的位,因為那是我們正在建模的東西:

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

所以, [h, l] << 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)

等等

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM