如何检查8位无符号字符中的设置位数?

时间:2015-06-06 23:07:57

标签: c embedded bits

所以我必须在C?

中找到unsigned char变量的设置位(在1上)

类似的问题是How to count the number of set bits in a 32-bit integer?但是它使用的算法不容易适应8位无符号字符(或者不明显)。

7 个答案:

答案 0 :(得分:5)

问题How to count the number of set bits in a 32-bit integer?中建议的算法可以简单地适应8位:

int NumberOfSetBits( uint8_t b )
{
     b = b - ((b >> 1) & 0x55);
     b = (b & 0x33) + ((b >> 2) & 0x33);
     return (((b + (b >> 4)) & 0x0F) * 0x01);
}

这只是将常数缩短为最低有效8位,并删除最后24位右移的情况。同样,它可以使用8位移位适应16位。注意,在8位的情况下,32位算法的机械适应导致冗余* 0x01,这可以省略。

答案 1 :(得分:4)

8位变量的最快方法是使用查找表。

构建一个包含256个值的数组,每个8位组合一个。每个值应包含其相应索引中的位数:

int bit_count[] = {
// 00 01 02 03 04 05 06 07 08 09 0a, ... FE FF
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, ..., 7, 8
};

计算组合数与查找bit_count数组中的值相同。这种方法的优点是速度非常快。

您可以使用一个简单的程序生成数组,该程序以缓慢的方式逐位计数:

for (int i = 0 ; i != 256 ; i++) {
    int count = 0;
    for (int p = 0 ; p != 8 ; p++) {
        if (i & (1 << p)) {
            count++;
        }
    }
    printf("%d, ", count);
}

demo that generates the table)。

如果您想为内存交换一些CPU周期,可以使用16字节查找表进行两次4位查找:

static const char split_lookup[] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
};

int bit_count(unsigned char n) {
    return split_lookup[n&0xF] + split_lookup[n>>4];
}

Demo

答案 2 :(得分:1)

计算不同于0的位数也称为Hamming Weight。在这种情况下,您正在计算1的数量。

Dasblinkenlight为您提供了表驱动的实现,Olaf为您提供了基于软件的解决方案。我认为你有两个其他潜在的解决方案。第一种是使用编译器扩展,第二种是使用具有来自C的内联汇编的ASM特定指令。

有关第一种选择,请参阅GCC的__builtin_popcount()。 (感谢Artless Noise)。

对于第二种选择,您没有指定嵌入式处理器,但我会在其基于ARM的情况下提供此功能。

某些ARM处理器具有VCNT指令,该指令为您执行计数。所以你可以用C进行内联汇编:

inline
unsigned int hamming_weight(unsigned char value) {
    __asm__ __volatile__ (
            "VCNT.8"
            : "=value"
            : "value"
    );

    return value;
}

另见Fastest way to count number of 1s in a register, ARM assembly

为了完整起见,这是Kernighan的位计数算法:

int count_bits(int n) {
    int count = 0;
    while(n != 0) {
        n &= (n-1);
        count++;
    }
    return count;
}

另见Please explain the logic behind Kernighan's bit counting algorithm

答案 3 :(得分:1)

我认为您正在为8位寻找汉明重量算法? 如果是,那么这是代码:

unsigned char in = 22; //This is your input number
unsigned char out = 0;
in = in - ((in>>1) & 0x55);
in = (in & 0x33) + ((in>>2) & 0x33);
out = ((in + (in>>4) & 0x0F) * 0x01) ;

答案 4 :(得分:1)

我做了一个优化版本。对于 32 位处理器,利用乘法、位移和掩码可以为同一任务编写更小的代码,尤其是当输入域很小(8 位无符号整数)时。

以下两个代码片段是等效的:

unsigned int bit_count_uint8(unsigned char x)
{
    x = ((((((x * 0x08040201UL) >> 3) & 0x11111111UL) *
        0x11111111UL) >> 28) & 0xF);
    return x;
}

/*
unsigned int bit_count_uint8(unsigned char x)
{
    x = x - ((x >> 1) & 0x55);
    x = (x & 0x33) + ((x >> 2) & 0x33);
    x = ((x + (x >> 4)) & 0x0F);
    return x;
}
*/

说明

我表示字节x的八位,从MSB到LSB,分别为abcdefgh

                               abcdefgh
*   00001000 00000100 00000010 00000001 (make 4 copies of x
---------------------------------------  with appropriate
abc defgh0ab cdefgh0a bcdefgh0 abcdefgh  bit spacing)
>> 3                                   
---------------------------------------
    000defgh 0abcdefg h0abcdef gh0abcde
&   00010001 00010001 00010001 00010001
---------------------------------------
    000d000h 000c000g 000b000f 000a000e
*   00010001 00010001 00010001 00010001
---------------------------------------
    000d000h 000c000g 000b000f 000a000e
... 000h000c 000g000b 000f000a 000e
... 000c000g 000b000f 000a000e
... 000g000b 000f000a 000e
... 000b000f 000a000e
... 000f000a 000e
... 000a000e
... 000e
    ^^^^ (Bits 31-28 will contain the sum of the bits
          a, b, c, d, e, f, g and h. Extract these
          bits and we are done.)

答案 5 :(得分:0)

也许不是最快,但很简单:

int count = 0;

for (int i = 0; i < 8; ++i) {
    unsigned char c = 1 << i;
    if (yourVar & c) {
        //bit n°i is set
        //first bit is bit n°0
        count++;
    }
}

答案 6 :(得分:0)

对于8/16位MCU,循环很可能比并行添加方法更快,因为这些MCU每条指令的移位不能超过一位,所以:

size_t popcount(uint8_t val)
{
    size_t cnt = 0;
    do {
        cnt += val & 1U;    // or: if ( val & 1 ) cnt++;
    } while ( val >>= 1 ) ;
    return cnt;
}

对于cnt的增量,您可能会进行分析。如果仍然太慢,使用进位标志(如果可用)可能值得一试。虽然我一般反对使用汇编程序优化,但这些算法是为数不多的好例外之一(仍然只是在C版本失败之后)。

如果您可以省略Flash,@ dasblinkenlight建议的查找表就像最快的方法。

只是一个提示:对于某些体系结构(特别是ARM和x86 / 64),gcc有一个内置的:__builtin_popcount(),你也可能想尝试一下(虽然它至少需要int)。这可能使用单个CPU指令 - 您无法更快,更紧凑。