位操作应该说从学C的时候就接触过,一开始以为没什么大用,但是现在在不断的学习和有了工作经验之后,发现这个东西真是个好东西,很多便捷操作都可以借助它来实现,并且很有效率。
参考文章:
《算法心得》参考的不多,里面很底层。
符号 | 描述 | 规则 |
& | 按位与 | 两个位都是1,结果才是1(1&1=1;1&0=0) |
| | 按位或 | 两个位都是0,结果才是0(1&0=1;0&0=0) |
^ | 按位异或 | 两个位相同为0,相异为1(1&0=1;1&1=0) |
~ | 按位取反 | 0变1,1变0(单目操作符,优先级高) |
<< | 左移 | 左移若干位,丢弃高位,低位补0 |
>> | 右移 | 右移若干位;无符号数,高位补0,有符号数,算数右移(补符号位)或者逻辑右移(补0) |
1. 针对右移,需要简单扩展一下。
逻辑右移:不考虑符号位,右移一位,左边补0;
算术右移:考虑符号位,右移一位,若符号位为1,左边补1,否则补0。(可以进行有符号位的除法,右移n位=除2的n次方)。
8的二进制:1100 1101。
逻辑右移:[0]110 0110。
算术右移:[1]110 0110。
2. 符合操作符
&=、|=、^=、<<=、>>=。
常用位操作
1.判断奇偶
奇数偶数只要根据最后一位是1还是0就能判断出,传统的操作比如(i % 2 == 0)来判断,位操作里可以用&来代替。实际测试代码来看,确实时间花费要少许多。
for(int i = 0; i < 101 ; i++){
if( (i & 1) == 0){
System.out.println(i);
}
}
2.交换2数
这个我在很早的面试宝典里就看见过了,心想,何必这么秀呢。
if(a != b){
a ^= b;
b ^= a;
a ^= b;
}
异或运算法则如下:
1. a ^ b = b ^ a;
2. a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
3. a ^ b ^ a = a ^ a ^ b = 0 ^ b = b(a和自己异或等于0,0和任意数异或还等于自己);
通过以上三个法则就能来推到两数交换了,很简单其实。
3.变换符号
这个其实没什么说头,就是当初学习正数负数的二进制操作时所说的取反+1操作的代码实现。
~a + 1
4.求绝对值
主要看当前的数是正数还是负数来决定,能决定的就是符号位,因此先右移得到,高1位,然后判断是0还是1,再做取反+1操作即可。
int i = a >> 31;
return (i == 0) ? a : ~a + 1;
但是比较苛刻的用法此处还能优化,,比如在第一点里的异或规则我们知道,a ^ 0 = a ,而 a ^ -1 = ~a(任何数和-1异或相当于取反操作,因为-1的二进制码是1111 1111,很特殊)。
加上我们i的取值在a右移31位后刚好是0或者-1,所以可以写成下面这种。
int i = a >> 31;
return ((a ^ i) - i);
//i 为0时 return ((a ^ 0)-0);
//i 为-1时 return ((a ^ -1) - (-1)) == (~a + 1);
5判断一个数是不是2的幂
我们发现2的幂的数的二进制都是最高位是1,其余都是0,那么如何判断这种呢?可以将高位1变成0,其余变成1,按位与操作,这样,如果还是0,说明是2的幂,如果不是0,说明不是。
return i & (i-1) && (i != 0);