计算以字节为单位设置的位数

时间:2012-03-30 20:22:12

标签: c++ templates bitcount

我感兴趣,这是以这种方式计算以字节为单位设置的位数的最佳方法

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的值时,它是否是最佳的?是否建议在代码中使用它?

12 个答案:

答案 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。

相关问题