簡體   English   中英

獲得 C++ std::bitset 的最低設置位的最快方法是什么?

[英]What is the fastest way to get the lowest set bit of a C++ std::bitset?

我注意到std::bitset沒有用於返回或彈出位集的最低設置位的函數(也沒有與此相關的最高設置位)。 完成此任務的最快方法是什么,特別是對於不能保證一定長度的 std::bitset 對象? 我查看了 g++ 編譯器擴展和C++20 數字庫,但沒有找到與我的問題相關的任何內容。

要考慮的兩種明顯方法是遍歷 bitset 的長度並使用operator[]或使用operator>>逐漸移動 bitset 直到找到第一個 set 位。

我將像大多數現代實現一樣使用尾隨零,以便輕松處理沒有設置位的情況。 要利用硬件計數尾隨零指令,我們可以使用to_ullong() ,但要使其工作,我們需要屏蔽該值以使其適合unsigned long long

#include <bitset>
#include <bit>
#include <climits>

template<size_t N>
size_t count_trailingzero(std::bitset<N> b)
{
    if (b.none())
        return N;                   // The whole bitset was zero

    const decltype(b) mask(-1ULL);  // Mask to get the lowest unsigned long long
    size_t tz = 0;                  // The number of trailing zero bits
    const int width = sizeof(unsigned long long)*CHAR_BIT;
    do {
        auto lsw = (b & mask).to_ullong();  // The least significant word
        auto lsb = std::countr_zero(lsw);   // Position of the least significant bit

        if (lsb < width)                    // Found the first set bit from right
            return tz + lsb;
        
        // A set bit was not found because the lsw is all zero
        // so we'll increase the number of trailing zero bits
        tz += width;

        // Shift the bitset to get the next higher significant word
        b >>= width;
    } while (b.any());

    return tz;
}

Godbolt 上的演示

這樣就不需要對單個位進行循環,並且可以使用硬件加速。 但它仍然不是最有效的方法,因為整個位集的每次迭代仍然需要屏蔽掉。 這就是為什么std::bitset通常不是對位進行操作的好工具,我在實踐中總是避免使用它們。 為位操作包裝一個數組的類在性能和靈活性上會好得多

你可以這樣做:

#include <bitset>
#include <iostream>
int main(int argc, char **argv) {
  std::bitset<32> your_bit{0B1010000};
  int lsb = your_bit[0];
  std::cout << lsb << '\n';
}

沒有.lsb().msb()成員函數,但std::bitset確實提供.size().test() (和.any() ,歸功於@phuctv以使用.any()超過.count() ),您可以用它來構造 lsb 和 msb 例程。

假設一個有效的std::bitset您可以使用.any()驗證至少一位設置為真(或者只檢查無符號值)。 在驗證至少一位為真后,只需從bit-0循環到bit-(bitset.size() - 1) ,使用.test()檢查設置位以獲得 LSB。 然后用相同的測試反向循環以找到 MSB。

一個簡短的實現是:

#include <iostream>
#include <bitset>

int main () {
  
  size_t i = 0;                 /* bit indiex */
  std::bitset<8> bits (236);    /* bitset '11101100' */
  
  if (!bits.any()) {  /* validate at least 1 bit set */
    std::cerr << "pop count is zero.\n";
    return 1;
  }
  
  /* loop bit 0 to bits.size() - 1 for LSB */
  do {
    if (bits.test(i))             /* test if bit set */
      break;
  } while (++i < bits.size());
  
  std::cout << "lsb in '" << bits << "' is: " << i << '\n';
  
  /* loop bit bits.size() - 1 to 0 for MSB */
  i = bits.size();
  while (i--) {
    if (bits.test(i))             /* test if bit set */
      break;
  }
  
  std::cout << "msb in '" << bits << "' is: " << i << '\n';
}

示例使用/輸出

$ ./bin//bitset_test
lsb in '11101100' is: 2
msb in '11101100' is: 7

擴展 std::bitset 並添加.lsb().msb()成員函數

除了簡單地編寫幾個函數之外,您還可以從std::bitset派生並將.lsb().msb()成員函數添加到派生類。

使用上述相同實現的簡短類聲明可以是:

template<size_t Nb>
class mybitset : public std::bitset<Nb> {
  
  std::bitset<Nb> bits;
  
 public:
  mybitset (const std::bitset<Nb>&b) : std::bitset<Nb>{b} { bits = b; }
  
  size_t lsb();     /* extend std::bitset with .lsb() and .msb() members */
  size_t msb();
  
  template<size_t NB>
  friend std::ostream& operator << (std::ostream& os, const mybitset<NB>& b);
};

然后你可以直接使用.lsb().msb()成員,例如

int main () {
  
  mybitset<8> bits (236);       /* derived class */
  
  if (!bits.any()) {  /* validate at least one bit set */
    std::cerr << "bitset value is zero -- zero pop count.\n";
    return 1;
  }
  
  /* output LSB and MSB */
  std::cout << "lsb in '" << bits << "' is: " << bits.lsb() <<
             "\nmsb in '" << bits << "' is: " << bits.msb() << '\n';
}

(相同的輸出)

有點遲到的答案,C++20 包含<bit> ,它具有您想要的功能。

特別是std::countr_zero ,它返回從最低有效位開始的連續 0 位的數量。

#include <bit>
#include <iostream>

// This requires C++20 (or later)

int main() {
  std::cout << "lsb of 0b0100u is " << std::countr_zero(0b0100u) << "\n";
}

// std::countr_zero(0b0100u) is 2.

暫無
暫無

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

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