C 代码计算“1”的数量 无符号字符中的位

发布于 07-16 06:24 字数 92 浏览 13 评论 0原文

我需要 C 代码来返回 C 中无符号字符中 1 的数量。如果不明显,我需要解释为什么它可以工作。 我找到了很多 32 位数字的代码,但没有找到太多用于无符号字符的代码。

I need C code to return the number of 1's in an unsigned char in C. I need an explanation as to why it works if it's not obvious. I've found a lot of code for a 32-bit number but not much for an unsigned char.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

别靠近我心2024-07-23 06:24:27
const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};

unsigned char CountOnes(unsigned char x)
{
    unsigned char results;
    results = oneBits[x&0x0f];
    results += oneBits[x>>4];
    return results
}

有一个知道 0 到 15 位数的数组。将每个半字节的结果相加。

const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};

unsigned char CountOnes(unsigned char x)
{
    unsigned char results;
    results = oneBits[x&0x0f];
    results += oneBits[x>>4];
    return results
}

Have an array that knows the number of bits for 0 through 15. Add the results for each nibble.

夏有森光若流苏2024-07-23 06:24:27

相同的代码适用于无符号字符。 循环测试它们的所有位。 请参阅

The same code will work for an unsigned char. Loop over all bits testing them. See this.

轮廓§2024-07-23 06:24:27

HACKMEM 在 3 个操作中使用此算法(大致翻译为 C) :(

bits = (c * 01001001001ULL & 042104210421ULL) % 017;

ULL是强制64位算术。这是需要的,只是勉强......这个计算需要33位整数。)

实际上,你可以用042104210021ULL替换第二个常量 code>,因为你只计算 8 位,但它看起来不太对称。

这是如何运作的? 按位思考 c,并记住 (a + b) % c = (a % c + b % c) % c(a | b) == a + b 当且仅当(a & b) == 0

  (c * 01001001001ULL & 042104210421ULL) % 017
  01   01001001001                01         1
  02   02002002002       02000000000         1
  04   04004004004          04000000         1
 010  010010010010            010000         1
 020  020020020020               020         1
 040  040040040040      040000000000         1  # 040000000000 == 2 ** 32
0100 0100100100100        0100000000         1
0200 0200200200200           0200000         1

如果您没有可用的 64 位算术,您可以将 c 分成半个字节并分别执行一半,需要 9 次运算。 这仅需要 13 位,因此使用 16 位或 32 位算术即可。

bits = ((c & 017) * 0421 & 0111) % 7 + ((c >> 4) * 0421 & 0111) % 7;

(c * 0421 & 01111) % 7
 1   0421      01    1
 2  01042   01000    1
 4  02104    0100    1
 8  04210     010    1

例如,如果c == 105 == 0b11001001

c == 0100
   |  040
   |  010
   |   01 == 0151
* 01001001001001ULL == 0100100100100
                     |  040040040040
                     |  010010010010
                     |   01001001001 == 0151151151151
& 0421042104210421ULL ==  0100000000
                       | 04000000000
                       |      010000
                       |          01 ==   04100010001
% 017                                == 4

c & 017      ==            8 | 1           ==                   011
011 * 0421   ==     8 * 0421 | 1 * 0421    == 04210 | 0421 == 04631
04631 & 0111 == 04210 & 0111 | 0421 & 0111 ==   010 | 01   ==   011
011 % 7      == 2

c >> 4       ==            4 | 2            ==                     06
06 * 0421    ==     4 * 0421 | 2 * 0421     == 02104 | 01042 == 03146
03146 & 0111 == 02104 & 0111 | 01042 & 0111 ==  0100 | 01000 == 01100
01100 % 7    == 2

2 + 2 == 4

HACKMEM has this algorithm in 3 operations (roughly translated to C):

bits = (c * 01001001001ULL & 042104210421ULL) % 017;

(ULL is to force 64-bit arithmetic. It's needed, just barely... this calculation requires 33-bit integers.)

Actually, you can replace the second constant with 042104210021ULL, since you're only counting 8 bits, but it doesn't look as nicely symmetrical.

How does this work? Think of c bit-wise, and remember that (a + b) % c = (a % c + b % c) % c, and (a | b) == a + b iff (a & b) == 0.

  (c * 01001001001ULL & 042104210421ULL) % 017
  01   01001001001                01         1
  02   02002002002       02000000000         1
  04   04004004004          04000000         1
 010  010010010010            010000         1
 020  020020020020               020         1
 040  040040040040      040000000000         1  # 040000000000 == 2 ** 32
0100 0100100100100        0100000000         1
0200 0200200200200           0200000         1

If you don't have 64-bit arithmetic available, you can split c up into nibbles and do each half, taking 9 operations. This only requires 13 bits, so using 16- or 32-bit arithmetic will work.

bits = ((c & 017) * 0421 & 0111) % 7 + ((c >> 4) * 0421 & 0111) % 7;

(c * 0421 & 01111) % 7
 1   0421      01    1
 2  01042   01000    1
 4  02104    0100    1
 8  04210     010    1

For example, if c == 105 == 0b11001001,

c == 0100
   |  040
   |  010
   |   01 == 0151
* 01001001001001ULL == 0100100100100
                     |  040040040040
                     |  010010010010
                     |   01001001001 == 0151151151151
& 0421042104210421ULL ==  0100000000
                       | 04000000000
                       |      010000
                       |          01 ==   04100010001
% 017                                == 4

c & 017      ==            8 | 1           ==                   011
011 * 0421   ==     8 * 0421 | 1 * 0421    == 04210 | 0421 == 04631
04631 & 0111 == 04210 & 0111 | 0421 & 0111 ==   010 | 01   ==   011
011 % 7      == 2

c >> 4       ==            4 | 2            ==                     06
06 * 0421    ==     4 * 0421 | 2 * 0421     == 02104 | 01042 == 03146
03146 & 0111 == 02104 & 0111 | 01042 & 0111 ==  0100 | 01000 == 01100
01100 % 7    == 2

2 + 2 == 4
冧九2024-07-23 06:24:27

请参阅位旋转黑客页面:http://graphics.stanford.edu/~seander/ bithacks.html#CountBitsSetKernighan

对于这个问题有很多好的解决方案。

此外,这个函数在其最简单的实现中是相当简单的。 您应该花时间学习如何执行此操作。

See the bit twiddling hacks page: http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan

there are many good solutions for this.

Also, this function in its simplest implementation is fairly trivial. You should take the time to learn how to do this.

孤星2024-07-23 06:24:27

对于像无符号字符一样小的整数,您可以使用小型查找表获得最佳性能。

我知道你提到的人口计数算法。 它们的工作原理是对小于寄存器中存储的整数的多个字进行算术。

这种技术称为 SWAR (http://en.wikipedia.org/wiki/SWAR) 。

欲了解更多信息,我建议您查看 hackersdelight 网站:www.hackersdelight.org。 他有示例代码并写了一本书,详细解释了这些技巧。

For a integer as small as an unsigned char you get best performance using a small lookup-table.

I know what population-count algorithms you're mentioning. They work by doing arithmetic of multiple words smaller than an integer stored in a register.

This technique is called SWAR (http://en.wikipedia.org/wiki/SWAR).

For more information I suggest you check out the hackers delight website: www.hackersdelight.org. He has example code and written a book that explains these tricks in detail.

剪不断理还乱2024-07-23 06:24:27

正如已经回答的那样,计算位的标准方法也适用于无符号字符。

例子:

    unsigned char value = 91;
int bitCount = 0;
while(value > 0)
{
    if ( value & 1 == 1 ) 
        bitCount++;
    value >>= 1;
}

As already answered, the standard ways of counting bits also work on unsigned chars.

Example:

    unsigned char value = 91;
int bitCount = 0;
while(value > 0)
{
    if ( value & 1 == 1 ) 
        bitCount++;
    value >>= 1;
}
夜血缘2024-07-23 06:24:27

unsigned char 是一个“数字”,就像 32 位浮点数或整数是一个“数字”一样,编译器认为它们代表的就是变化。

如果你把一个字符想象成它的位:

01010011(8位);

您可以通过执行以下操作来计算设置的位:

取值,假设为 x,并取 x % 2,余数将为 1 或 0。也就是说,取决于 char 的字节顺序,最左边或最右边少量。 将余数累加到一个单独的变量中(这将是设置位数的结果)。

然后>> (右移)1 位。

重复直到 8 位被移位。

从我的伪代码中实现 c 代码应该非常简单,但基本上

public static int CountSetBits(char c)
{
    int x = 0;
    int setBits = 0;
    while (x < 7)
    {
       setBits = setBits + c % 2;
       c = c >> 1;
       x = x + 1;
    }
}

an unsigned char is a "number" in just the same way that a 32-bit float or integer is a "number", what the compiler deems them to represent is what changes.

if you picture a char as its bits:

01010011 (8 bits);

you can count the set bits by doing the following:

take the value, lets say x, and take x % 2, the remainder will be either 1 or 0. that is, depending on the endianness of the char, the left or right most bit. accumulate the remainder in a separate variable (this will be the resulting number of set bits).

then >> (right shift) 1 bit.

repeat until 8 bits have been shifted.

the c code should be pretty simple to implement from my pseudocode, but basically

public static int CountSetBits(char c)
{
    int x = 0;
    int setBits = 0;
    while (x < 7)
    {
       setBits = setBits + c % 2;
       c = c >> 1;
       x = x + 1;
    }
}
飘然心甜2024-07-23 06:24:27

根据 Ehemient 的帖子,我们有无分支的 8 位版本。 它以十六进制表示。

typedef unsigned char       UINT8;
typedef unsigned short      UINT16;
typedef unsigned long long  UINT64;
int hammingWeight8( const UINT8& c)
{
    return ( c* 0x8040201ULL & 0x11111111)%0xF;
}

应用两次,我们有16位版本,需要9次操作。

int hammingWeight16( const UINT16& c)
{
    return ((c & 0xFF)* 0x8040201ULL & 0x11111111)%0xF + 
             ((c >> 8)* 0x8040201ULL & 0x11111111)%0xF;
}

这里我写了一个变体16位版本,需要64位寄存器和11次操作。 看起来并不比前一个好,但它只是使用了 1 模运算。

int hammingWeight16( const UINT16& c)
{
    UINT64  w;
    w= (((( c* 0x8000400020001ULL)>> 3) & 0x1111111111111111)+14)%0xF;
    return (c!=0)*(w+1+(c==0xFFFF)*15);
}

base on Ephemient's post, we have the no branched 8 bits version. It is in hexadecimal expression.

typedef unsigned char       UINT8;
typedef unsigned short      UINT16;
typedef unsigned long long  UINT64;
int hammingWeight8( const UINT8& c)
{
    return ( c* 0x8040201ULL & 0x11111111)%0xF;
}

Apply it twice, we have a 16bits version, which needs 9 operations.

int hammingWeight16( const UINT16& c)
{
    return ((c & 0xFF)* 0x8040201ULL & 0x11111111)%0xF + 
             ((c >> 8)* 0x8040201ULL & 0x11111111)%0xF;
}

Here I write a variant 16bits version which needs 64bits registers and 11 operations. It seems not better than the previous one, but it just uses 1 modulo operation.

int hammingWeight16( const UINT16& c)
{
    UINT64  w;
    w= (((( c* 0x8000400020001ULL)>> 3) & 0x1111111111111111)+14)%0xF;
    return (c!=0)*(w+1+(c==0xFFFF)*15);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文