簡體   English   中英

用於計算unsigned char中“1”位數的C代碼

[英]C code to count the number of '1' bits in an unsigned char

我需要C代碼在C中的unsigned char中返回1的數量。如果不明顯,我需要解釋它為什么有效。 我找到了很多32位數的代碼,但對於unsigned char卻沒有多少代碼。

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位數的數組。為每個半字節添加結果。

相同的代碼適用於unsigned char。 循環遍歷測試它們的所有位。 看到這個

HACKMEM在3個操作中有這個算法(大致翻譯成C):

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

ULL強制使用64位算術。這是必需的,只是勉強...這個計算需要33位整數。)

實際上,您可以用042104210021ULL替換第二個常量,因為您只計算8位,但它看起來不那么對稱。

這是如何運作的? 想一想c ,請記住(a + b) % c = (a % c + b % c) % c(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

如果您沒有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

請參閱bit twiddling hacks頁面: http//graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan

有很多很好的解決方案。

此外,這個函數在其最簡單的實現中是相當簡單的。 你應該花時間學習如何做到這一點。

對於與unsigned char一樣小的整數,使用小型查找表可獲得最佳性能。

我知道你提到的人口計數算法。 它們通過對小於存儲在寄存器中的整數的多個字進行算術來工作。

這種技術稱為SWAR( http://en.wikipedia.org/wiki/SWAR )。

有關更多信息,我建議您查看黑客高興網站:www.hackersdelight.org。 他有示例代碼並編寫了一本書,詳細解釋了這些技巧。

unsigned char是一個“數字”,與32位浮點數或整數是“數字”的方式相同,編譯器認為它們代表的是變化。

如果你把char描繪成它的位:

01010011(8位);

您可以通過執行以下操作來計算設置位:

取值,比如x,取x%2,余數為1或0.也就是說,取決於字符的字節順序,左邊或最右邊的位。 將余數累積在一個單獨的變量中(這將是結果的設置位數)。

然后>>(右移)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;
    }
}

正如已經回答的那樣,計數位的標准方法也適用於無符號字符。

例:

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

基於Ephemient的帖子,我們有無分支的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;
}

在這里,我編寫了一個16bits版本,需要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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM