2010年1月5日 星期二

X = X & X-1 Trick

最近在寫練習的時候看到這個有趣的習題, 相信很多人已經知道了,
就是在 2's complement 裡面, X = X & X-1, 會移除 X 裡面最右邊的 1,
在 C 裡面, 只要在 unsigned (或者全為正數)的變數裡面, 就可以用這個方法,
主要原因, 我想應該是在 2 進位的體系下, 只要讓某個值少 1, 就會讓那個值最後的一個1的右邊數值產生借位, 而造成了一個很有趣的 Pattern如下:



01111111 127
01111110 126
.
.
.
01011000 88
01010111 87

所以 X & X-1 會把 least significant 1-bit 給 & 成 0.

然而, 這個方法在 Sign-and-magnitude 的表示法裡會踢到鐵板,
因為他的正負值差別只在 sign bit. 所以並不適用此規則,
ex:
10000010 -2
10000011 -3

x = -2; x &= x-1 ==> 可以看到, x 做完運算還是等於 -2.

最後來看個範例, 來計算一個 unsigned 變數裡有幾個 bit 被 turned on.

正常的方法來檢驗每個 bit
int bitcount(unsigned x)
{
int b;

for (b=0; x !=0; x >>= 1)
if (x & 01)
b++;
return b;
}


用 X & X-1 trick 來做檢驗
int quickBitcount(unsigned x)
{
int count = 0;

while (x) {
x &= (x-1);
count++;
}
return count;
}


完畢, 感謝大家收看.

6 則留言:

  1. 還是不會用 閱讀更多 的功能~ 囧rz

    回覆刪除
  2. http://realtimecollisiondetection.net/blog/?p=78
    延伸閱讀
    (我最不擅長這些了 @@)

    回覆刪除
  3. 稍微分析一下quickBitcount,假設x中0,1出現的機率一樣(也就是平均下來會有n/2個0和n/2個1,n = sizeof(x)),則需要的基本運算個數分別是:
    CMP n/2
    SHIFT 0
    AND n/2
    ADD(SUB) (n/2) * 2 /*for x-1 and count++*/

    正常的bitcount的分析就比較複雜些,有空再來看看:D

    回覆刪除
  4. 兩個bitcount的分析來囉,一樣假設x中每個bit為0 or 1的機率各半:
    _________quickBitcount____bitcount
    CMP______(n/2) + 1________(n-1) - 1/2^n
    SFT__________0___________(n-2) - 1/2^n
    AND__________n/2_________(n-2) - 1/2^n
    ADD__________n___________(n-2) - 1/2^n
    /*n = sizeof(x)*/
    /*一堆底線因為不知道怎麼在意見做格式化輸出耶*/
    假設x是32 bits, 則下表可以看出兩個演算法的差別:
    _________quickBitcount____bitcount
    CMP__________17__________31
    SFT___________0__________30
    AND__________16__________30
    ADD__________32__________30

    在RISC電腦上,以上的運算都屬於Arithmatic Logic Unit(ALU),所需運算時間差不多,結論是quickBitcount大約比bitcount快上一倍.

    回覆刪除