在新浪微博上看到一篇文章寫位運算的寫的很深入,文章鏈接見末尾,特此mark。
0.位運算的種類
符號 | 名稱 | 運算規則 |
---|---|---|
& | 與 | 兩個位都為1時,結果才為1,否則都為0 |
l | 或 | 兩個位都是0時,結果才為1,否則都為0 |
^ | 異或 | 兩個位都相同為0,否則為1 |
~ | 取反 | 0->1,1->0 |
<< | 左移 | 各二進位全部左移若干位,高位丟棄,低位補0 |
>> | 右移 | 各二進位全部右移若干位,對無符號數,高位補0,有符號數,各編譯器處理方法不一樣,有的補符號位(算術右移),有的補0(邏輯右移) |
markdown中|我不知道怎么表示,就用l了
1.判斷兩個整數是否符號相反
bool oppositeSigns(int x, int y)
{
bool f = ((x ^ y) < 0); // true iff x and y have opposite signs
return f;
}
2.判斷一個數的符號
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.求一個數的絕對值
int absulteValue(int v){
unsigned int r; // the result goes here
int const mask = v >> (sizeof(int) * CHAR_BIT - 1);
/*explain:在計算機中,負數以其正值的補碼形式表達。
什么叫補碼呢?這得從原碼,反碼說起。
原碼:一個整數,按照絕對值大小轉換成的二進制數,稱為原碼。
比如 00000000 00000000 00000000 00000101 是 5的 原碼。
反碼:將二進制數按位取反,所得的新二進制數稱為原二進制數的反碼。
取反操作指:原為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 互為反碼。
補碼:反碼加1稱為補碼。
也就是說,要得到一個數的補碼,先得到反碼,然后將反碼加上1,所得數稱為補碼。
比如:00000000 00000000 00000000 00000101 的反碼是:11111111 11111111 11111111 11111010。
那么,補碼為:
11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011
所以,-5 在計算機中表達為:11111111 11111111 11111111 11111011。轉換為十六進制:0xFFFFFFFB。
所以獲取一個負數的絕對就是將負數取反在+1即可
所以最初的形式可寫成
int my_abs(int a)
{
int i = a >> 31;
return i == 0 ? a : (~a + 1);
}
現在再分析下。對于任何數,與0異或都會保持不變(異或是兩個位相同為0,相異位1),與-1即0xFFFFFFFF異或就相當于取反。
因此,v與mask異或后再減mask(因為mask為0或-1,所以減mask即是要么加0要么加1)也可以得到絕對值。所以可以對上面代碼優化下:
r = (v ^ mask) - mask;
*/
r = (v + mask) ^ mask;
//or
//r = (v ^ mask) - mask;
return r;
}
4.計算最大最小值
int max(int x, int y){
/*
*利用一點,對于任何數a,因為0的二進制全為0,有a&0 = 0,因為-1的二進制全為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.計算一個值是否是2的次冪
bool isPowerOfTwo(int x){
//如果一個數是2的次冪,那么除首位,其余為肯定都為0,減一后其余為為1
bool f = x && !(x & (x - 1));
return f;
}
6.交換
void swap(int &x, int &y){
/*異或運算有兩個特性:
1、一個數異或本身恒等于0,如5^5恒等于0;
2、一個數異或0恒等于本身,如5^0恒等于5。*/
if (x != y)
{
x ^= y;
y ^= x;
x ^= y;
}
}
7.更多技巧,參考文章:http://graphics.stanford.edu/~seander/bithacks.html
后面的內容沒那個太深,暫時用到的可能性不太,如果后期有業余精力再去深入研究。