繁体   English   中英

计算 C 中字节的平均位数

[英]Calculate the average bits of byte in C

我尝试编写一个 function 来计算字节的平均位数。

float AvgOnesOnBinaryString (int x)

例如:

-252 是 11111111 11111111 11111111 00000100

所以 function 返回 6.25 因为 ( 8+8+8+1) / 4 = 6.25

我必须使用 function 来计算 char 中的位数:

int countOnesOnBinaryString (char x){
    int bitCount = 0;
    while(x > 0)
    {
        if ( x & 1 == 1 )
            bitCount++;
        x = x>>1;
    }
    return bitCount;
}

我试过了:

float AvgOnesOnBinaryString (int x){
    float total = 0;
    total += countOnesOnBinaryString((x >> 24));
    total += countOnesOnBinaryString((x >> 16));
    total += countOnesOnBinaryString((x >> 8));
    total += countOnesOnBinaryString(x);
    return total/4;
}

但我得到的答案是 0.25 而不是 6.25 可能是什么问题?

更新

我无法更改 AvgOnesOnBinaryString function 签名。

C 语言允许编译器将char定义为有符号或无符号类型。 我怀疑它是在您的平台上签名的,这意味着像0xff这样的字节可能被解释为-1 这意味着countOnesOnBinaryString中的x > 0测试产生错误,因此countOnesOnBinaryString(0xff)将返回 0 而不是正确的值 8。

您应该更改countOnesOnBinaryString以采用unsigned char而不是char类型的参数。

出于某种相关的原因,将AvgOnesOnBinaryString的参数更改为unsigned int也是一个好主意。 或者更好的是,来自<stdint.h>uint32_t ,因为您的代码假定输入值是 32 位,并且 (unsigned) int 允许具有其他大小。

有一种算法可以更快地计算unsigned变量中1的位数。 在 32 位 integer 中只需要 5 次迭代。 我将在 C 中向您展示一个完整的 64 位unsigned数,因此您可能会猜到该模式及其工作原理(如下所述):

uint64_t 
no_of_1_bits(uint64_t the_value)
{
    the_value = ((the_value & 0xaaaaaaaaaaaaaaaa) >>  1) + (the_value & 0x5555555555555555);
    the_value = ((the_value & 0xcccccccccccccccc) >>  2) + (the_value & 0x3333333333333333);
    the_value = ((the_value & 0xf0f0f0f0f0f0f0f0) >>  4) + (the_value & 0x0f0f0f0f0f0f0f0f);
    the_value = ((the_value & 0xff00ff00ff00ff00) >>  8) + (the_value & 0x00ff00ff00ff00ff);
    the_value = ((the_value & 0xffff0000ffff0000) >> 16) + (the_value & 0x0000ffff0000ffff);
    the_value = ((the_value & 0xffffffff00000000) >> 32) + (the_value & 0x00000000ffffffff);

    return the_value;
}

1的位数将在the_value的 64 位值中。 如果将结果除以 8,对于unsigned long整数,您将获得平均每字节 1 位(当复制符号位时,请注意使用带signed chars进行移位,因此您的算法永远不会因负数而停止)

对于 8 位字节,算法简化为:

uint8_t 
no_of_1_bits(uint8_t the_value)
{
    the_value = ((the_value & 0xaa) >> 1) + (the_value & 0x55);
    the_value = ((the_value & 0xcc) >> 2) + (the_value & 0x33);
    the_value = ((the_value & 0xf0) >> 4) + (the_value & 0x0f);

    return the_value;
}

同样,1 的位数在变量the_value中。

该算法的想法是在第一步中生成两位累加器中每对位的总和(我们将一对累加器的左位向右移动以使其与右位对齐,然后将它们加在一起,并且对于每对位是并行的)。 由于累加器是两位,因此不可能溢出(因此从一对位到下一位永远不会进位,我们使用完整的 integer 作为一系列两位寄存器来相加)

然后我们将每对位相加到一个四位累加器中,再一次,它永远不会溢出……让我们对我们得到的半字节做同样的事情,并将它们相加到 8 位的寄存器中……如果不可能用两位溢出一个 4 位累加器,用 4 位加法溢出一个 8 位累加器是更不可能的......并继续直到你将单词的左半部分与右半部分相加。 您最终以字长的一个全长寄存器中的所有位的总和结束。

容易,不是吗? :)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM