一個unsigned int 數的二進位制表示中有多少個1
第一種是很容易想到的採用迴圈的方式並且與1進行位與運算,具體程式碼如下。
1unsigned int GetBitNumOfOne_ByLoop1(unsigned int nValue)
2{
3 const unsigned int nNumOfBitInByte =8;
4 unsigned int nBitMask =1;
5 unsigned int nBitNum =0;
6 for(unsigned int i =0 ; i <sizeof(nValue) * nNumOfBitInByte ; i++)
7 {
8 (0< (nValue & nBitMask))
9 nBitMask<<=1;
10 }11 return nBitNum;
12}13unsigned int GetBitNumOfOne_ByLoop2(unsigned int nValue)
14{
15 const unsigned int nNumOfBitInByte =8;
16 unsigned int nBitMask =1;
17 unsigned int nBitNum =0;
18 for(unsigned int i =0 ; i <sizeof(nValue) * nNumOfBitInByte ; i++)
19 {
20 (0<
21 nValue>>=1;
22 }23 return nBitNum;
24}
這兩種做法很相像,區別就是在對nBitMask進行左移還是對nValue進行右移。
當然了以上的兩個方法存在一個問題:不管如何這個函式肯定要迴圈32次(對於32平臺來說)。
那又沒有更好的方法?當然有,請看下面:
1unsigned int GetBitNumOfOne_ByLoop3(unsigned int nValue)
2{
3 unsigned int nBitNum =0;
4 while(0< nValue)
6 nValue &=(nValue -1);
7 nBitNum++;
8 } 9 return nBitNum;
10}
假如使用引數12345(二進位制是11000000111001)呼叫該函式,該函式的執行情況如下:
第一次進入迴圈
0 < 11000000111001
11000000111001 &= (11000000111001 - 1) 之後 nValue 的值是 11000000111000
nBitNum 的值是1
經過本次迴圈之後11000000111001 變成了 11000000111000 比之前少了一個1
第二次進入迴圈
0 < 11000000111000
11000000111000 &= (11000000111000 - 1) 之後 nValue 的值是 11000000110000
nBitNum 的值是2
經過本次迴圈之後11000000111000 變成了 11000000110000 比之前少了一個1
第三次進入迴圈
0 < 11000000110000
11000000110000 &= (11000000110000 - 1) 之後 nValue 的值是 11000000100000
nBitNum 的值是3
經過本次迴圈之後11000000110000 變成了 11000000100000 比之前少了一個1
經過以上3次迴圈情況的說明,我相信你一定看出了些什麼吧。nValue &=(nValue -1),這句
程式碼實際上就是把nValue 的某位及其以後的所有位都變成0,當nValue最後變成0的時候迴圈結束,
且nBitNum 記錄的就是1的個數。
上面的做法已經很不錯了,但是作為程式設計師的你是否會有疑問,“還有其他的方法嗎?”。
有,當然有!請看下面的程式碼:
1unsigned int GetBitNumOfOne(unsigned int nValue)
2{
3 nValue = ((0xaaaaaaaa& nValue)>>1) + (0x55555555& nValue);
4 nValue = ((0xcccccccc& nValue)>>2) + (0x33333333& nValue);
5 nValue = ((0xf0f0f0f0& nValue)>>4) + (0x0f0f0f0f& nValue);
6 nValue = ((0xff00ff00& nValue)>>8) + (0x00ff00ff& nValue);
7 nValue = ((0xffff0000& nValue)>>16) + (0x0000ffff& nValue);
8
9 return nValue;
10}
假如你是第一次看到這些程式碼,你是否能看明白?呵呵,本人第一次看到這些程式碼的時候看了好久才感覺
好像有點明白。下面我就以一個例子來說明上面的程式碼是如何做到的。
假如引數是0xffffffff。
第一行程式碼:
nValue = ((0xaaaaaaaa &nValue)>>1) + (0x55555555& nValue);
a的二進位制表示是:1010
5的二進位制表示是:0101
0xffffffff 與 0xaaaaaaaa進行與運算之後是0x10101010101010101010101010101010
然後再進行左移操作後是0x01010101010101010101010101010101
0x55555555 & nValue進行與運算之後是0x01010101010101010101010101010101
然後0x01010101010101010101010101010101 &0x01010101010101010101010101010101
得到0x10101010101010101010101010101010
我們把得到的結果分成16組0x10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
每組的10單獨來看是不是十進位制的2
我們0x01010101010101010101010101010101和0x01010101010101010101010101010101這兩個數也按照上面的方法分成
16個組0x01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
0x01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
那麼這兩個數的每一個組都是01 那麼兩個01裡面有幾個1,是不是2個。這是2是不是二進位制的10,然後16個10組合起來是不是
0x10101010101010101010101010101010
第二行程式碼:
nValue = ((0xcccccccc &nValue)>>2) + (0x33333333& nValue);
此時nValue是0x10101010101010101010101010101010
c的二進位制表示是:1100
3的二進位制表示是:0011
0xcccccccc 與 nValue)進行與運算之後是0x10001000100010001000100010001000
然後再進行左移操作後是0x00100010001000100010001000100010
0x33333333 & nValue進行與運算之後是0x00100010001000100010001000100010
然後0x00100010001000100010001000100010 &0x00100010001000100010001000100010
得到0x01000100010001000100010001000100
我們把得到的結果分成8組0x0100 0100 0100 0100 0100 0100 0100 0100
每組的0100單獨來看是不是十進位制的4 總共有多少個4?是不是8個,8×4=32。
以下的程式碼:
nValue = ((0xf0f0f0f0 &nValue)>>4) + (0x0f0f0f0f& nValue);
nValue = ((0xff00ff00 &nValue)>>8) + (0x00ff00ff& nValue);
nValue = ((0xffff0000 &nValue)>>16) + (0x0000ffff& nValue);
請自己按照上面的方法做一遍,就會發現規律:第一次32位數分成32組,第二次分成16組,第三次分成8,第四次分成4,第五次分成2組。
如果還是不明白請多看看,然後多選擇幾個引數進行試驗,多試幾次肯定會明白的。