我感兴趣,这是以这种方式计算以字节为单位设置的位数的最佳方法
template< unsigned char byte > class BITS_SET
{
public:
enum {
B0 = (byte & 0x01) ? 1:0,
B1 = (byte & 0x02) ? 1:0,
B2 = (byte & 0x04) ? 1:0,
B3 = (byte & 0x08) ? 1:0,
B4 = (byte & 0x10) ? 1:0,
B5 = (byte & 0x20) ? 1:0,
B6 = (byte & 0x40) ? 1:0,
B7 = (byte & 0x80) ? 1:0
};
public:
enum{RESULT = B0+B1+B2+B3+B4+B5+B6+B7};
};
在运行时知道byte的值时,它是否是最佳的?是否建议在代码中使用它?
答案 0 :(得分:18)
对于一个字节的数据,考虑速度和内存消耗的最佳方式:
uint8_t count_ones (uint8_t byte)
{
static const uint8_t NIBBLE_LOOKUP [16] =
{
0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4
};
return NIBBLE_LOOKUP[byte & 0x0F] + NIBBLE_LOOKUP[byte >> 4];
}
从for循环调用此函数应该可以在大多数系统上生成相当高效的程序。它非常通用。
答案 1 :(得分:17)
对于8位值,只需使用256个元素的查找表。
对于较大尺寸的输入,它稍微不那么琐碎。 Sean Eron Anderson在他的Bit Twiddling Hacks page上有几个不同的功能,都具有不同的性能特征。没有一个全能最快的版本,因为它取决于处理器的性质(管道深度,分支预测器,缓存大小等)和您正在使用的数据。
答案 2 :(得分:10)
为什么不使用标准库?这样,最佳方式应该由实现决定,并且可能比您可以实际编写的任何符合标准的代码更好。例如,如果您使用的是x86,则会编译为单个指令,但仅限于you're targeting CPUs that support it。
#include <bitset>
#include <iostream>
int main() {
unsigned char bitfield = 17;
std::cout << std::bitset<8>(bitfield).count() <<
std::endl;
}
答案 3 :(得分:4)
对于单个字节值,最快的方法是将答案存储在使用该值索引的256字节数组中。例如,bits_set[] = {0, 1, 1, 2, ...
答案 4 :(得分:2)
“最快做bitcount的方法”的通常答案是“查找数组中的字节”。这种方式适用于字节,但您需要为它支付实际的内存访问权限。 如果你只是偶尔这样做一次,它可能是最快的,但如果你只是偶尔做一次,你就不需要最快。
如果你做了很多,你最好将字节分成单词或双字,并对这些进行快速的bitcount操作。这些往往是纯算术,因为你不能在数组中实际查找32位值来获得它的bitcount。相反,您可以通过巧妙的方式移动和屏蔽来组合值。
这样做的一个很好的聪明技巧来源是Bit Hacks。
这是在那里发布的用于计算C中32位字的位的方案:
unsigned int v; // count bits set in this (32-bit value)
unsigned int c; // store the total here
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
答案 5 :(得分:2)
为什么不左移并掩盖其余的?
int countBits(unsigned char byte){
int count = 0;
for(int i = 0; i < 8; i++)
count += (byte >> i) & 0x01; // Shift bit[i] to the first position, and mask off the remaining bits.
return count;
}
通过简单计算被计数值中有多少位,然后在计数器循环中使用该值,可以很容易地调整它以处理任何大小的整数。这一切都非常简单。
int countBits(unsigned long long int a){
int count = 0;
for(int i = 0; i < sizeof(a)*8; i++)
count += (a >> i) & 0x01;
return count;
}
答案 6 :(得分:1)
int count(int a){ return a == 0 ? 0 : 1 + count(a&(a-1)); }
答案 7 :(得分:1)
#include <iostream>
#include <climits> // for CHAR_BIT (most likely to be 8)
#include <cstring> // for memset
#include <new>
static const int DUMMY = -1;
// first approch : activate the O(8) function in first get try... after that its O(1);
class bitsInByteflyLUT
{
typedef unsigned char byte;
public:
bitsInByteflyLUT(); //CTOR - throws std::bad_alloc
~bitsInByteflyLUT(); //DTOR
int Get_bitsInByte(byte _byte);
private:
// CLASS DATA
int* flyLUT;
// PRIVATE FUNCTIONS
int bitsInByte(byte _byte);
// O(8) for finding how many bits are ON in a byte.
// answer can be between 0 to CHAR_BIT.
bitsInByteflyLUT(const bitsInByteflyLUT & _class); // COPY CTOR - forbidden
const bitsInByteflyLUT & operator= (const bitsInByteflyLUT& _class);
// ASSIGN OPERATOR - forbidden
};
bitsInByteflyLUT::bitsInByteflyLUT()
{
size_t nIndexes = 1 << CHAR_BIT;
try
{
flyLUT = new int[nIndexes];
}
catch (std::bad_alloc& ba)
{
throw;
}
memset(flyLUT, DUMMY, sizeof(int)*nIndexes);
}
bitsInByteflyLUT::~bitsInByteflyLUT()
{
delete[] flyLUT;
}
int bitsInByteflyLUT::Get_bitsInByte(byte _byte)
{
if (flyLUT[_byte] == DUMMY) // if its first time we try to get answer for this char.
{
flyLUT[_byte] = bitsInByte(_byte); // O(8)
}
return flyLUT[_byte]; // O(1)
}
int bitsInByteflyLUT::bitsInByte(byte _byte)
{
byte nBits = CHAR_BIT;
byte counter = 0;
byte mask = 1;
while(nBits--)
{
if(mask & _byte)
{
++counter;
}
mask <<= 1;
}
return counter;
}
int main ()
{
using std::cout;
using std::endl;
bitsInByteflyLUT flut;
for (unsigned int i = 0; i < (1 << CHAR_BIT); i += 1)
{
cout << i << " " << flut.Get_bitsInByte(i) << endl;
}
return 0;
}
答案 8 :(得分:1)
C++20 从头 std::popcount
<bit>
std::popcount(0b1101u)
将返回 3
有关详细信息,请参阅 https://en.cppreference.com/w/cpp/numeric/popcount。
答案 9 :(得分:0)
#include <iostream>
#include <ctime>
using namespace std;
int count1s(unsigned char byte)
{
if (byte == 0)
{
return 0;
}
if (byte & 0x01)
{
return 1+count1s(byte>>1);
}
return count1s(byte>>1);
}
int count1s2(unsigned char byte)
{
static const int ones[256] =
{0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,
3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,
4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,
3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,
4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,
6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,
2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,
4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,
3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,
6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,
5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,
4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,
5,6,6,7,5,6,6,7,6,7,7,8};
return ones[(int)byte];
}
int main()
{
time_t start = clock();
int c = count1s(205);
time_t end = clock();
cout<<"count1: "<<c<<" time: "<<double(end-start)<<endl;
start = clock();
c = count1s2(205);
end = clock();
cout<<"count2: "<<c<<" time: "<<double(end-start)<<endl;
return 0;
}
答案 10 :(得分:0)
使用C ++ 17,您可以使用constexpr lambda预先计算查找表。相对于准备好的复制粘贴表,更容易推断出其正确性。
#include <array>
static constexpr auto bitsPerByteTable = [] {
std::array<uint8_t, 256> table{};
for (decltype(table)::size_type i = 0; i < table.size(); i++) {
table.at(i) = table.at(i / 2) + (i & 1);
}
return table;
}();
答案 11 :(得分:0)
在gcc中,您可以使用__builtin_popcount(unsigned)函数。
它应该针对目标硬件平台有效地使用最佳解决方案。
对于-march = core-avx2(与我的CPU最高兼容的级别),使用了popcntl x86_64汇编指令,该指令在硬件中完成。
使用默认的x86_64指令集,将调用popcntl函数,该函数实现最佳C(聪明的黑客)算法。
无符号long和unsigned long long还有__builtin_popcountl和__builtin_popcountll。