簡體   English   中英

將屏蔽的位移到lsb

[英]Shift masked bits to the lsb

當您and一些帶有掩碼的數據一起使用時,您會得到與數據/掩碼大小相同的結果。 我想做的是將結果中的掩碼位(掩碼中有1)移到右側,使它們彼此相鄰,然后我可以對它們執行CTZ(計數尾隨零) 。

我不知道如何命名這樣的程序,所以Google讓我失敗了。 優選地,該操作不應是循環解決方案,這必須是盡可能快的操作。

這是MS Paint制作的令人難以置信的圖像。 在此處輸入圖片說明

此操作稱為“ 壓縮權” 從Haswell起,它在BMI2中作為PEXT指令實現為PEXT指令。

不幸的是,沒有硬件支持,這是一個非常煩人的操作。 當然,有一個明顯的解決方案,就是將位逐個循環移動,這是Hackers Delight給出的:

unsigned compress(unsigned x, unsigned m) {
   unsigned r, s, b;    // Result, shift, mask bit. 

   r = 0; 
   s = 0; 
   do {
      b = m & 1; 
      r = r | ((x & b) << s); 
      s = s + b; 
      x = x >> 1; 
      m = m >> 1; 
   } while (m != 0); 
   return r; 
} 

但是還有另一種方法,也由Hackers Delight提供,它執行的循環次數較少(迭代次數為對數,以位數為單位),但每次迭代更多:

unsigned compress(unsigned x, unsigned m) {
   unsigned mk, mp, mv, t; 
   int i; 

   x = x & m;           // Clear irrelevant bits. 
   mk = ~m << 1;        // We will count 0's to right. 

   for (i = 0; i < 5; i++) {
      mp = mk ^ (mk << 1);             // Parallel prefix. 
      mp = mp ^ (mp << 2); 
      mp = mp ^ (mp << 4); 
      mp = mp ^ (mp << 8); 
      mp = mp ^ (mp << 16); 
      mv = mp & m;                     // Bits to move. 
      m = m ^ mv | (mv >> (1 << i));   // Compress m. 
      t = x & mv; 
      x = x ^ t | (t >> (1 << i));     // Compress x. 
      mk = mk & ~mp; 
   } 
   return x; 
}

注意,那里的很多值僅取決於m 由於您只有512個不同的蒙版,因此您可以預先計算這些蒙版,並將代碼簡化為以下形式(未經測試)

unsigned compress(unsigned x, int maskindex) {
   unsigned t; 
   int i; 

   x = x & masks[maskindex][0];

   for (i = 0; i < 5; i++) {
      t = x & masks[maskindex][i + 1]; 
      x = x ^ t | (t >> (1 << i));
   } 
   return x; 
}

當然,所有這些都可以通過展開而變成“非循環”,第二種和第三種方式可能更適合於此。 但是,這有點作弊。

您可以使用類似於此處所述的逐乘打包技術。 這樣,您就不需要任何循環,並且可以按任何順序混合位。

例如,使用上面的掩碼0b10101001 == 0xA9和8位數據abcdefgh (其中ah是8位),您可以使用以下表達式獲得0000aceh

uint8_t compress_maskA9(uint8_t x)
{
    const uint8_t mask1 = 0xA9 & 0xF0;
    const uint8_t mask2 = 0xA9 & 0x0F;
    return (((x & mask1)*0x03000000 >> 28) & 0x0C) | ((x & mask2)*0x50000000 >> 30);
}

在這種特定情況下,在乘法步驟中加法(導致意外的進位)時,這4位有一些重疊,因此我將它們分為2部分,第一個部分提取a和c位,然后e和h將是在后一部分中提取。 還有其他分割位的方法,例如a&h然后c&e。 您可以將結果與ideone上的 Harold函數進行比較

一次乘法的另一種方法

const uint32_t X = (x << 8) | x;
return (X & 0x8821)*0x12050000 >> 28;

我通過復制這些位使它們之間的距離更遠,從而留出足夠的空間來避免進位。 這通常比分成2個乘法好


如果您希望結果的位反轉(即heca0000 ),則可以輕松地相應更改幻數

// result: he00 | 00ca;
return (((x & 0x09)*0x88000000 >> 28) & 0x0C) | (((x & 0xA0)*0x04800000) >> 30);

或者您也可以同時提取3位e,c和a,分別將h保留(如我上面提到的,通常有多種解決方案),並且只需要一個乘法

return ((x & 0xA8)*0x12400000 >> 29) | (x & 0x01) << 3; // result: 0eca | h000

但是可能會有更好的選擇,例如上面的第二個片段

const uint32_t X = (x << 8) | x;
return (X & 0x2881)*0x80290000 >> 28

正確性檢查: http : //ideone.com/PYUkty

對於更大數量的蒙版,您可以預先計算與這些蒙版相對應的幻數並將它們存儲在數組中,以便您可以立即查找它們以供使用。 我用手算了那些面具,但是你可以自動做


說明

我們有abcdefgh & mask1 = a0c00000 乘以magic1

    ........................a0c00000
 ×  00000011000000000000000000000000 (magic1 = 0x03000000)
    ────────────────────────────────
    a0c00000........................
 + a0c00000......................... (the leading "a" bit is outside int's range
    ────────────────────────────────  so it'll be truncated)
r1 = acc.............................

=> (r1 >> 28) & 0x0C = 0000ac00

同樣,我們將abcdefgh & mask2 = 0000e00h乘以magic2

  ........................0000e00h
× 01010000000000000000000000000000 (magic2 = 0x50000000)
  ────────────────────────────────
  e00h............................
+ 0h..............................
  ────────────────────────────────
r2 = eh..............................

=> (r2 >> 30) = 000000eh

將它們結合在一起,我們將獲得預期的結果

((r1 >> 28) & 0x0C) | (r2 >> 30) = 0000aceh

這是第二個片段的演示

                  abcdefghabcdefgh
&                 1000100000100001 (0x8821)
  ────────────────────────────────
                  a000e00000c0000h
× 00010010000001010000000000000000 (0x12050000)
  ────────────────────────────────
  000h
  00e00000c0000h
+ 0c0000h
  a000e00000c0000h
  ────────────────────────────────
= acehe0h0c0c00h0h
& 11110000000000000000000000000000
  ────────────────────────────────
= aceh

對於逆序情況:

                  abcdefghabcdefgh
&                 0010100010000001 (0x2881)
  ────────────────────────────────
                  00c0e000a000000h
x 10000000001010010000000000000000 (0x80290000)
  ────────────────────────────────
  000a000000h
  00c0e000a000000h
+ 0e000a000000h
  h
  ────────────────────────────────
  hecaea00a0h0h00h
& 11110000000000000000000000000000
  ────────────────────────────────
= heca

有關:

暫無
暫無

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

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