众所周知,计算机中的所有数据都是以二进制形式存储的,位运算直接作用在内存中的二进制数据,所以运算速度非常快。
首先明白一点,位运算只能用于整型数据,int 类型占4个字节,1个字节占8位,其次,要清楚6种位运算符:
&:与,只有1&1=1,其余都为0;
| :或,只有0|0=0,其余都为1;
^ :异或,两个为相同为0,相异为1;
~:取反,各位0变1,1变0;
<<:左移,各二进制位全部左移若干位,高位丢弃,低位补0;
>>:右移,各二进制位全部右移若干位,对无符号数,高位补0,低位移除,对有符号数,java是算数右移,即高位补符号位,符号位:0正数,1负数
在计算机中负数是以其正值的补码形式存储的。
原码:对整型数直接转化为二进制数,最高位为符号位
反码:正数的反码与其原码相同;负数的反码是除符号位外对其原码逐位取反。
补码:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
着重分析一下左移和右移,如下:
int i=15;//15=8+4+2+1,即 00000000 00000000 00000000 00001111
int j=-15;//11111111 11111111 11111111 11110001,负数存的是补码,
System.out.println(i<<2);// 00000000 00000000 00000000 00111100=60
System.out.println(j<<2);//11111111 11111111 11111111 11000100注意这是补码,
//输出的时候还要转换为原码,除符号位外逐位取反+1
//10000000 00000000 00000000 00111100=-60
System.out.println(i>>2);//00000000 00000000 00000000 00000011=3
System.out.println(j>>2);//11111111 11111111 11111111 11111100注意这是补码,除符号位外逐位取反+1
//10000000 00000000 00000000 00000100=-4
常见的利用位运算进行的操作:
1)《剑指Offer》中给出过这样一道题:写一个函数求两个整数之和,要求在函数体内不能用算数运算符加、减、乘、除。
思路:对于十进制数相加我们其实做了三步:
(1) sum=各位相加不进位;
(2)carry=做进位,取出进位数
(3)result=sum+carry;
对于二进制数求和同样适用,所以函数如下:
public static int AddWithoutArithmetic(int num1, int num2)
{
//什么时候退出递归呢?当两数相加没有发生进位的时候
if(num2==0){
return num1;
}
//各位相加不进位,0+0=0 1+1=0;0+1=1 1+0=1 和位运算符异或(^)相同
int sum=num1^num2;
//做进位,取出进位值
int carry=(num1&num2)<<1;//只有1+1才会发生进位,和运算符与(&)相同,并且二进制中的进位相当于左移一位
return AddWithoutArithmetic(sum,carry);
}
2)判断奇偶数
public static String OddOrEven(int n){
if((n&1)==0){//n=4,00000000 00000000 00000000 00000100&00000000 00000000 00000000 00000001=00000000 00000000 00000000 00000000
return "even";
}else{//n=5;101&001=001
return "odd";
}
}
3)交换两数
public static void exchange(int a,int b){
a^=b;//a=a^b
b^=a;//b=b^a=b^(a^b)=(b^b)^a=0^a=a
a^=b;//a=a^b=(a^b)^(a)=(a^a)^b=0^b=b
}
4)变换正负号(取反+1)
System.out.println(~7+1);
//00000000 00000000 00000000 00000111=7
//11111111 11111111 11111111 11111000=~7
//11111111 11111111 11111111 11111001这是补码,转换为整数时,需要除符号为外逐位取反,再加1
//10000000 00000000 00000000 00000111=-7
5)求绝对值
public static int abs(int a){
int i=a>>31;//取符号位
return i==0?a:(~a+1);//或者return (a^i)-i;因为a^0=a;a^(-1)=~a;这种方式没用任何判断表达式包括三目表达式
}