![](/img/trans.png)
[英]How to query if any bit in a range is set in a 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;
}
這樣就不需要對單個位進行循環,並且可以使用硬件加速。 但它仍然不是最有效的方法,因為整個位集的每次迭代仍然需要屏蔽掉。 這就是為什么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.