<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Hopes

    Start Here..

     

    求整數(shù)中比特為1的二進制位數(shù) (轉)

    求整數(shù)中比特為1的二進制位數(shù)

    分類: C/C++ 2306人閱讀 評論(3) 收藏 舉報

     

    好幾次在CSDN上看到別人討論如何求出一個整數(shù)的二進制表示中狀態(tài)為1的比特位數(shù)。今天寫了個程序把從網(wǎng)上看來的加上自己想出來的共有5種方法測試了一下,覺得好玩,就寫了這篇博客。

    首先簡單介紹一下這五種方法。

    第一種:最簡單的,通過移位操作逐位測試并計數(shù),不妨稱為“逐位測試法”;

    第二種:注意到對于“單比特二進制”而言,其狀態(tài)與數(shù)值“相同”。即對于單個比特的“數(shù)”而言,為0即表示數(shù)值0,“全部為1”即表示數(shù)值1(注意多比特數(shù)值顯然沒有這個特點,比如一個字節(jié)有8個比特,當8個比特全為1時并不代表整數(shù)8,而代表255)。利用這一特點,從單個比特開始,以相鄰的一位、兩位、四位、八位和十六位為分組,一組一組地相加并逐步累計,最終得出結果;不妨稱為“分組統(tǒng)計法”;

    第三種:注意到一個整數(shù)減1的會把最后一位1變成0,而其后的0全變成1,利用這一特點,把一個數(shù)不斷與它減1后的結果做“按位與”,直到它變成0,那么循環(huán)的次數(shù)便是其狀態(tài)為1的比特位數(shù)。不妨稱之為“循環(huán)減一相與法”;

    第四種:考虛到多數(shù)處理器都提供“找到第一個為1的比特位”這種指令,在我的PC上直接調(diào)用X86處理器的BSFBSR指令,每次直接找到第一個不為0的位并消掉,直到該數(shù)變成0,那么循環(huán)的次數(shù)即為狀態(tài)為1的比特位數(shù)。這種不妨稱為“匯編嵌入法”;

    第五種,一個字節(jié)一共有256種狀態(tài),將每一個取值所對應的0比特位數(shù)的事先寫在程序中(注意這些數(shù)值是有規(guī)律的),也耗不了太多內(nèi)存,然后程序運行的時候,把整數(shù)的四個字節(jié)拆開逐字節(jié)查表并相加,這種可稱為“查表法”。

     

    以下是程序。程序中對應以上五種方法的函數(shù)分別命名為f1f5。另外還有三個函數(shù),correctness_test通過幾個簡單而又特殊的取值測試各個函數(shù)的正確性,相當于一個小單元測試;performance_test則分別將這5個函數(shù)作用于一億個隨機整數(shù)同進行性能測試;prepare_test_data則是準備1億個隨機整數(shù)的程序(這個函數(shù)實際并沒有為測試數(shù)據(jù)提供足夠的隨機性,但這一點對性能測試的結果應該沒有太大影響)

    1. #include <iostream>    
    2. #include <cstdlib>   
    3. #include <ctime>   
    4. using namespace std;   
    5.   
    6. int f1(unsigned int num);  
    7. int f2(unsigned int num);  
    8. int f3(unsigned int num);  
    9. int f4(unsigned int num);  
    10. int f5(unsigned int num);  
    11.   
    12. void correctness_test();  
    13. void performance_test();  
    14. void prepare_test_data(unsigned int data[], int size);  
    15.   
    16. int main() {  
    17.     correctness_test();  
    18.     performance_test();  
    19.     return 0;   
    20. }  
    21.   
    22. int f1(unsigned int num) {  
    23.     int count = 0;  
    24.     while(num) {  
    25.         if(num & 1) ++count;  
    26.         num >>= 1;  
    27.     }  
    28.     return count;  
    29. }  
    30.   
    31. int f2(unsigned int num) {  
    32.     const unsigned int M1 = 0x55555555;  
    33.     const unsigned int M2 = 0x33333333;  
    34.     const unsigned int M4 = 0x0f0f0f0f;  
    35.     const unsigned int M8 = 0x00ff00ff;  
    36.     const unsigned int M16 = 0x0000ffff;  
    37.   
    38.     num = (num & M1) + ((num >> 1) & M1);  
    39.     num = (num & M2) + ((num >> 2) & M2);  
    40.     num = (num & M4) + ((num >> 4) & M4);  
    41.     num = (num & M8) + ((num >> 8) & M8);  
    42.     num = (num & M16) + ((num >> 16) & M16);  
    43.     return num;  
    44. }  
    45.   
    46. int f3(unsigned int num) {  
    47.     int count = 0;  
    48.     while(num) {  
    49.         num &= (num - 1);  
    50.         ++count;  
    51.     }  
    52.     return count;  
    53. }  
    54.   
    55. int f4(unsigned int num) {  
    56.     int count = 0;  
    57.     while(num) {  
    58.         int n;  
    59.         __asm {  
    60.             bsr eax, num  
    61.             mov n, eax  
    62.         }  
    63.         ++count;  
    64.         num ^= (1 << n);  
    65.     }  
    66.     return count;  
    67. }  
    68.   
    69. int f5(unsigned int num) {  
    70.     static const signed char TABLE[256] = {   
    71.         0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,  
    72.         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,  
    73.         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,  
    74.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    75.         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,  
    76.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    77.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    78.         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,  
    79.         1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,  
    80.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    81.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    82.         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,  
    83.         2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,  
    84.         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,  
    85.         3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,  
    86.         4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8  
    87.     };  
    88.   
    89.     unsigned char* p = reinterpret_cast<unsigned char*>(&num);  
    90.     int count = 0;  
    91.     while(p != reinterpret_cast<unsigned char*>(&num + 1)) {  
    92.         count += TABLE[*p++];         
    93.     }  
    94.     return count;  
    95. }  
    96.   
    97. void correctness_test() {  
    98.     unsigned int test_data[] = {0, 1, 2, 3, 0x01234567, 0x89abcdef, 0xffffffff};  
    99.     unsigned int corect_result[] = {0, 1, 1, 2, 12, 20, 32};  
    100.   
    101.     int (*fn[])(unsigned int) = {f1, f2, f3, f4, f5};  
    102.     for(int i = 0; i < sizeof(fn) / sizeof(*fn); ++i) {  
    103.         for(int j = 0; j < sizeof(test_data) / sizeof(*test_data); ++j) {  
    104.             if(fn[i](test_data[j]) != corect_result[j]) {  
    105.                 cout << "f" << (i + 1) << " failed!" << endl;  
    106.                 exit(-1);  
    107.             }  
    108.         }  
    109.     }  
    110.     cout << "All methods passed correctness test." << endl;  
    111. }  
    112.   
    113. void performance_test() {  
    114.     const int TEST_DATA_SIZE = 100000000;  
    115.     unsigned int* test_data = new unsigned int[TEST_DATA_SIZE];  
    116.     prepare_test_data(test_data, TEST_DATA_SIZE);  
    117.   
    118.     int (*fn[])(unsigned int) = {f1, f2, f3, f4, f5};  
    119.     for(int i = 0; i < sizeof(fn) / sizeof(*fn); ++i) {  
    120.         clock_t start = clock();  
    121.         for(int j = 0; j < TEST_DATA_SIZE; ++j) {  
    122.             fn[i](test_data[j]);  
    123.         }  
    124.         int ticks = clock() - start;  
    125.         double seconds = ticks * 1.0 / CLOCKS_PER_SEC;  
    126.   
    127.         cout << "f" << (i + 1) << " consumed " << seconds << " seconds." << endl;  
    128.     }  
    129.     delete[] test_data;  
    130. }  
    131.   
    132. void prepare_test_data(unsigned int* data, int len) {  
    133.     srand(0);  
    134.     for(int i = 0; i < len; ++i) {  
    135.         data[i] = static_cast<unsigned int>(rand() * 1.0 / RAND_MAX * 0xffffffff);  
    136.     }  
    137. }  

     

    在我的機器上(AMD Phenom 8560處理器,Windows XP SP2),使用Visual C++ 2008 Express Edition編譯并運行(Release版),某一次得到的輸出如下:

    All methods passed correctness test.

    f1 consumed 14.156 seconds.

    f2 consumed 1.032 seconds.

    f3 consumed 4.656 seconds.

    f4 consumed 13.687 seconds.

    f5 consumed 1.422 seconds.

    從結果來看,最慢的是第一種“逐位測試法”,最快的是第二種“分組統(tǒng)計法”。兩者相差近14倍!

    第三種“循環(huán)減一相與法”表現(xiàn)也很不錯,雖然跟最快的相比遜色很多,但比最慢的強多了;

    第四種“匯編嵌入法”,表面上看,其復雜度是跟數(shù)值中1的位數(shù)相關,這一點與方法三一樣。而不像方法一那樣復雜度跟整個數(shù)的位數(shù)有關。但其表現(xiàn)并不令人滿意,結果幾乎跟方法一一樣差,而無法跟方法三相比。查了一下指令手冊,發(fā)現(xiàn)BSR指令并不是一條固定周期的指令,作用于32位整數(shù)時,快的時候它只需幾個CPU時鐘周期,慢的時候需要40幾個時鐘周期,我想它極有可能是在CPU內(nèi)部通過類似于“逐位查找”的微命令實現(xiàn)的。

    第五種“查表法”的表現(xiàn)倒讓人相當滿意,性能逼近方法二。注意我只用了基于字節(jié)編碼的表,如果實際使用中需要這種運算,我們完全可以構造一個基于unsigned short編碼的表,這樣一個表將占用64K內(nèi)存,在現(xiàn)代PC上仍屬小菜一碟,而每個32位數(shù)只需要把前后兩半查表的結果一加即可。那樣一來,其性能會不會比方法二還要強呢?有興趣的朋友可以試試。:P

    最后,或許有朋友會問:第四種方法中既然采用嵌入?yún)R編,為何不把整個函數(shù)都用匯編來寫呢?那樣或許效率還會好一些。但那對其它幾種方法來說是不公平的,因為所有的方法都可以改用匯編來寫。所以,在這個函數(shù)中我只是把依賴于特定處理器(X86)、且無法使用C++語言及其標準庫直接實現(xiàn)的部分用匯編實現(xiàn),其它的計算仍然用C++語言寫出。剩下的事情,跟其它幾種方法的實現(xiàn)一樣——讓編譯器看著辦吧,它愛怎么優(yōu)化就怎么優(yōu)化。

    posted on 2013-11-11 18:57 ** 閱讀(230) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導航:
     

    導航

    統(tǒng)計

    公告

    你好!

    常用鏈接

    留言簿(2)

    隨筆檔案

    文章分類

    文章檔案

    新聞檔案

    相冊

    收藏夾

    C#學習

    友情鏈接

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲成AV人片久久| 久久亚洲色一区二区三区| 久久精品国产亚洲AV麻豆网站| 抽搐一进一出gif免费视频| 国产成人亚洲精品影院| 人妻巨大乳hd免费看| 色噜噜AV亚洲色一区二区| eeuss影院ss奇兵免费com| 亚洲中文字幕丝袜制服一区| xxxx日本在线播放免费不卡| 久久精品国产亚洲精品| 国产精品免费αv视频| 久久久影院亚洲精品| 在线美女免费观看网站h| 亚洲成在人线中文字幕| 成年网站免费视频A在线双飞| 精品亚洲成A人无码成A在线观看| 在线观看免费为成年视频| 欧美亚洲精品一区二区| 综合久久久久久中文字幕亚洲国产国产综合一区首 | 亚洲高清免费视频| 亚洲V无码一区二区三区四区观看| 一级毛片免费观看不卡视频| 亚洲国产精品免费在线观看| 免费看又爽又黄禁片视频1000| 日韩色日韩视频亚洲网站| 国产成人麻豆亚洲综合无码精品| 国产精品免费高清在线观看| 亚洲丝袜中文字幕| 亚洲 国产 图片| 久久免费精品视频| 久久久久久亚洲精品影院| 亚洲成a人片在线观看国产| 少妇人妻偷人精品免费视频| 亚洲熟女综合色一区二区三区 | 性xxxxx免费视频播放| 羞羞漫画在线成人漫画阅读免费| 亚洲国产综合无码一区| 成年女人午夜毛片免费看| aa级毛片毛片免费观看久| 亚洲中文字幕无码av在线|