快速判斷一個32位的字中是否存在值爲"0"的byte

原文:http://blogread.cn/it/article.php?id=5908&f=sinat

首先爲什麼要做這樣的判斷呢?

    當你要strcpy活着strcmp或者hash一個字符串的時候,傳統的方法是每個byte進行比較。以strcpy爲例,當一個字符串比較長,我們用32(或者64位)的字長進行copy的話,一次拷貝會拷貝4個byte,能節省很多時間(忽略內存對齊等情況)。

    但是,使用32位的字長進行拷貝一個難點就是判斷字符串的結尾,因爲字符串長度不一定是4的整數倍,每次從內存中取4個byte,我們需要判斷這4個byte中是否有某個byte是0,從而判斷字符串是否結束。

    最傳統的做法就是對每個字節進行一次判斷,或者將所有字節乘起來,看結果是否是0:http://www.spongeliu.com/

unsigned char * p = (unsigned char *) & v;  
bool hasNoZeroByte = *p && *(p + 1) && *(p + 2) && *(p + 3);

    上面的代碼需要12步操作,並且需要若干乘指令,效率不高。

    一種傳統有效的方法是通過對每個byte進行一次掩碼的操作來判斷是否存在0:

bool hasNoZeroByte = ((v & 0xff) && (v & 0xff00) && (v & 0xff0000) && (v & 0xff000000));

    上面的操作相當於取出每一個byte,並進行與操作,最終判斷是否是0,這樣做需要進行7次操作。

    那麼,是否有更快的方法呢? 看下面的表達式:

unsigned int v; // 32-bit word to check if any 8-bit byte in it is 0
bool hasZeroByte = ~((((v & 0x7F7F7F7F) + 0x7F7F7F7F) | v) | 0x7F7F7F7F);

    這種方法進行了5次操作來完成整個工作!具體是怎麼做到的,讓我們仔細來看:

    首先,將v同 0x7f7f7f7f 進行&操作,目的是將v中每個byte的最高位清零;

    然後,再加上 0x7f7f7f7f 的目的是讓每個byte的低7位溢出,這個時候只要每個byte的低7位不全是0,那麼就會溢出;

    隨後,我們再將得到的數同原先的v進行一個“按位或”的操作,這樣每個byte的最高位都會被置爲1,除非這個byte初始爲0(若初始爲0,則第二步的時候不會溢出,第三步的時候0|0還是0);

    再然後,我們再講得到的數同 0x7f7f7f7f “按位或”,則如果初始的數不包含0byte,我們就會得到0xffffffff,否則我們得不到這個數;

    最後,進行一個“按位否”操作,就會得到一個0或者非0。

    我們用兩個例子來更直觀的說明,先舉一個不包含0byte的32位數,以 0x5FF23D6E 爲例:

0x5FF23D6E & 0x7F7F7F7F = 0x5f723d6e;  //每個byte的高位全清0
0x5f723d6e + 0x7F7F7F7F = 0xdef1bced; //對每個byte的低7位進行溢出,只有當一個byte是0或者是“10000000”的時候不會溢出
0xdef1bced | 0x5FF23D6E = 0xdff3bdef; //或上原來的數,這是除非一個byte初始是0,否則最高位都會是1
0xdff3bdef | 0x7F7F7F7F = 0xffffffff; //將所有bit置爲1
~ 0xffffffff = 0; //得到結果

    再使用一個包含0byte的32位數爲例,以 0x5FF2006E 爲例:

0x5FF2006E & 0x7F7F7F7F = 0x5f72006e;  //每個byte的高位全清0
0x5f72006e + 0x7F7F7F7F = 0xdef17fed; //對每個byte的低7位進行溢出,這個時候因爲第三個byte是0,所以這個byte的最高位不是1
0xdef17fed | 0x5FF2006E = 0xdff37fef; //或上原來的數,第三個byte最高位不是1
0xdff37fef | 0x7F7F7F7F = 0xffff7fff; //第三個byte最高位不是1,其他所有位都是1
~ 0xffff7fff = 0x8000; //得到結果

    這種方法在一些字符串操作的性能優化,尤其是當大量字符串需要被哈希、拷貝時還是比較有效的。

    參考資料:http://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord

發佈了20 篇原創文章 · 獲贊 21 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章