在新浪微博上看到一篇文章写位运算的写的很深入,文章链接见末尾,特此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
后面的内容没那个太深,暂时用到的可能性不太,如果后期有业余精力再去深入研究。