最近开发需要将一个数据做奇偶校验,首先就是要计算出这个数据中bit位为1的个数,有以下几种算法可以达到要求:
我直接上代码,然后分析。
uint16_t get_one_in_data_1(uint16_t data)
{
uint16_t n = 0;
while (data > 0)
{
if (data & 0x01)
n++;
data >>= 1;
}
return n;
}
我们依次将输入数据的最低位同bit1进行&运算,如果最低位为1,则n++,然后移位比较下一位。这段代码的精妙之处在于,如果一个i数据的高位是0时,立即结束while循环,这样就提高了效率。注意:这个函数是可以传递32位uint32_t整形数据类型的。直接修改形参就可以了。
先上代码,然后分析。
uint16_t get_one_in_data_2(uint16_t data)
{
uint16_t n;
for (n = 0; data; n++)
data &= data - 1;
return n;
}
这段代码的思想是,二进数据只有0和1构成,当我们对原数据减一操作时,最低为为0的数据减一后都变为了1,然后&操作后可以消除这些位,举例来说:b'1000
减一操作后位b'0111
,然后再做&操作,结果就为0,这样就判断出该数据中只有一个1,同理其他数据也是一样。该算法的好处是不需要while和for两重循环,提高了执行效率。这样做显然要优于算法1。同样,形参也可以是32位整形变量。
先上代码,然后分析。
const unsigned char numbits_lookup_table[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
};
uint16_t get_one_in_data_3(uint16_t data)
{
uint16_t n = 0;
n = numbits_lookup_table[data & 0x00ff];
n += numbits_lookup_table[data>>8 & 0x00ff];
return n;
}
没错就是效率最高的查表法,时间复杂度只有O(1),查表法不需要循环,不需要判断,执行效率最高,但是占用ROM较多,适合存储大一点的场合使用,但是以目前的处理器水平来说,这点存储是可以忽略不记的,所以,推荐大家使用查表法来做。
三种算法都在vs2017环境测试通过。
三种方法各有千秋,如果最求简洁,可以使用方法2,如果最求高效,可以使用方法3。当然了,还有很多算法可以做到,这里就不再一一介绍。有兴趣的读者可以自己尝试其他算法。