在新浪微博上看到一篇文章寫位運(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è)余精力再去深入研究。