位運(yùn)算常見(jiàn)技巧

在新浪微博上看到一篇文章寫位運(yùn)算的寫的很深入,文章鏈接見(jiàn)末尾,特此mark。

0.位運(yùn)算的種類

符號(hào) 名稱 運(yùn)算規(guī)則
& 兩個(gè)位都為1時(shí),結(jié)果才為1,否則都為0
l 兩個(gè)位都是0時(shí),結(jié)果才為1,否則都為0
^ 異或 兩個(gè)位都相同為0,否則為1
~ 取反 0->1,1->0
<< 左移 各二進(jìn)位全部左移若干位,高位丟棄,低位補(bǔ)0
>> 右移 各二進(jìn)位全部右移若干位,對(duì)無(wú)符號(hào)數(shù),高位補(bǔ)0,有符號(hào)數(shù),各編譯器處理方法不一樣,有的補(bǔ)符號(hào)位(算術(shù)右移),有的補(bǔ)0(邏輯右移)

markdown中|我不知道怎么表示,就用l了


1.判斷兩個(gè)整數(shù)是否符號(hào)相反


bool oppositeSigns(int x, int y)
{
    bool f = ((x ^ y) < 0); // true iff x and y have opposite signs
    
    return f;
}

2.判斷一個(gè)數(shù)的符號(hào)

bool isUnSignInt(int v){
    
    int sign;   // the result goes here
    
    // CHAR_BIT is the number of bits per byte (normally 8).
    sign = -(v < 0);  // if v < 0 then -1, else 0.
    // or, to avoid branching on CPUs with flag registers (IA32):
    sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
    // or, for one less instruction (but not portable):
    sign = v >> (sizeof(int) * CHAR_BIT - 1);
    
    return sign == 1;
}

3.求一個(gè)數(shù)的絕對(duì)值

int absulteValue(int v){

    unsigned int r;  // the result goes here
    int const mask = v >> (sizeof(int) * CHAR_BIT - 1);
    
    /*explain:在計(jì)算機(jī)中,負(fù)數(shù)以其正值的補(bǔ)碼形式表達(dá)。
     什么叫補(bǔ)碼呢?這得從原碼,反碼說(shuō)起。
     原碼:一個(gè)整數(shù),按照絕對(duì)值大小轉(zhuǎn)換成的二進(jìn)制數(shù),稱為原碼。
     比如 00000000 00000000 00000000 00000101 是 5的 原碼。
     反碼:將二進(jìn)制數(shù)按位取反,所得的新二進(jìn)制數(shù)稱為原二進(jìn)制數(shù)的反碼。
     取反操作指:原為1,得0;原為0,得1。(1變0; 0變1)
     比如:將00000000 00000000 00000000 00000101每一位取反,得11111111 11111111 11111111 11111010。
     稱:11111111 11111111 11111111 11111010 是 00000000 00000000 00000000 00000101 的反碼。
     反碼是相互的,所以也可稱:
     11111111 11111111 11111111 11111010 和 00000000 00000000 00000000 00000101 互為反碼。
     補(bǔ)碼:反碼加1稱為補(bǔ)碼。
     也就是說(shuō),要得到一個(gè)數(shù)的補(bǔ)碼,先得到反碼,然后將反碼加上1,所得數(shù)稱為補(bǔ)碼。
     比如:00000000 00000000 00000000 00000101 的反碼是:11111111 11111111 11111111 11111010。
     那么,補(bǔ)碼為:
     11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011
     所以,-5 在計(jì)算機(jī)中表達(dá)為:11111111 11111111 11111111 11111011。轉(zhuǎn)換為十六進(jìn)制:0xFFFFFFFB。
     
     所以獲取一個(gè)負(fù)數(shù)的絕對(duì)就是將負(fù)數(shù)取反在+1即可
     所以最初的形式可寫成
     int my_abs(int a)
     {
     int i = a >> 31;
     return i == 0 ? a : (~a + 1);
     }
     
     現(xiàn)在再分析下。對(duì)于任何數(shù),與0異或都會(huì)保持不變(異或是兩個(gè)位相同為0,相異位1),與-1即0xFFFFFFFF異或就相當(dāng)于取反。
     因此,v與mask異或后再減mask(因?yàn)閙ask為0或-1,所以減mask即是要么加0要么加1)也可以得到絕對(duì)值。所以可以對(duì)上面代碼優(yōu)化下:
     
     r = (v ^ mask) - mask;
     */
    
    
    r = (v + mask) ^ mask;
    
    //or
    //r = (v ^ mask) - mask;

    return r;
}

4.計(jì)算最大最小值

int max(int x, int y){
    
    /*
     *利用一點(diǎn),對(duì)于任何數(shù)a,因?yàn)?的二進(jìn)制全為0,有a&0 = 0,因?yàn)?1的二進(jìn)制全為1,有a&(-1) = a
     如果x>y,x<y的值為0,-(x<y)也為0,(x ^ y) & 0 = 0, x ^ 0 = x。
     如果x<y,x<y的值為1,-(x<y)為-1,(x ^ y) & (-1) = x ^ y, x ^ (x ^ y) = y。
     于是能求得較大值。較小值類似。
     
     
     If you know that INT_MIN <= x - y <= INT_MAX, then you can use the following, which are faster because (x - y) only needs to be evaluated once.
     r = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // min(x, y)
     r = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1))); // max(x, y)
     */
    
    return x ^ ((x ^ y) & -(x < y));

}

5.計(jì)算一個(gè)值是否是2的次冪

bool isPowerOfTwo(int x){
    
    //如果一個(gè)數(shù)是2的次冪,那么除首位,其余為肯定都為0,減一后其余為為1
    
    bool f = x && !(x & (x - 1));
    
    return f;
}

6.交換

void swap(int &x, int &y){
    
    /*異或運(yùn)算有兩個(gè)特性:
    1、一個(gè)數(shù)異或本身恒等于0,如5^5恒等于0;
    2、一個(gè)數(shù)異或0恒等于本身,如5^0恒等于5。*/

    if (x != y)
    {
        x ^= y;
        y ^= x;
        x ^= y;
    }
    
}

7.更多技巧,參考文章:http://graphics.stanford.edu/~seander/bithacks.html

后面的內(nèi)容沒(méi)那個(gè)太深,暫時(shí)用到的可能性不太,如果后期有業(yè)余精力再去深入研究。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容