位运算
前言
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。
了解位运算之前,让我们首先一下二进制及其原码、反码、补码,方便我们容易的了解位运算。
1. 什么是二进制?
二进制是计算技术中广泛采用的一种数制,二进制数据采用0与1两个数码来表示的数。它的基数是2,进位规则是"逢二进一",借位规则是“借一当二”,由18世纪德国数理哲学大师莱布尼兹发现。
20世纪被称作第三次科技革命的重要标志之一的计算机的发明与应用,因为数字计算机只能识别和处理由‘0’.‘1’符号串组成的代码。其运算模式正是二进制。19世纪爱尔兰逻辑学家乔治布尔对逻辑命题的思考过程转化为对符号"0’’.’‘1’'的某种代数演算,二进制是逢2进位的进位制。0、1是基本算符。因为它只使用0、1两个数字符号,非常简单方便,易于用电子方式实现。
2. 原码、补码、反码
首先,你要知道这些:
二进制的最高位是符号位(“0”代表正数,“1”代表负数);
Java中没有无符号数;
计算机以整数的补码进行运算。
2.1 原码(将一个整数转换成二进制)
以int类型示例,int类型占4个字节,共32位
例如,2 的原码为:00000000 00000000 00000000 00000010
-2的原码为:10000000 00000000 00000000 00000010
2.2 反码
正数的反码:与原码相同
负数的反码:原码的符号位不变,其他位取反
例如,-2 的反码为:11111111 11111111 11111111 11111101
2.3 补码
正数的补码:与原码相同
负数的补码:反码+1
例如,-2 的补码为:01111111 11111111 11111111 11111110
位运算符号
含义 | Pascal语言 | C语言 | C#语言 | Java | 易语言 |
---|---|---|---|---|---|
按位与 | a and b | a & b | a & b | a & b | 位与(a,b) |
按位或 | a or b | a | b | a | b | a | b | 位或(a,b) |
按位异或 | a xor b | a ^ b | a ^ b | a ^ b | 位异或(a,b) |
按位取反 | not a | ~a | ~a | ~a | 位取反(a) |
左移 | a shl b | a << b | a << b | a << b | 左移(a,b) |
带符号右移 | a shr b | a >> b | a >> b | a >> b | / |
无符号右移 | / | / | / | a >>> b | / |
运算说明
1. 按位与 &:两位都为1,结果为1
例如,2&3 = 2
2 的原码为: 00000000 00000000 00000000 00000010
3 的原码为: 00000000 00000000 00000000 00000011
2&3 原码为: 00000000 00000000 00000000 00000010 = 2
2. 按位或 |:至少一位为1,结果为1
例如,2|3 = 3
2 的原码为: 00000000 00000000 00000000 00000010
3 的原码为: 00000000 00000000 00000000 00000011
2|3 原码为: 00000000 00000000 00000000 00000011 = 3
3. 按位异或 ^:两位一个为1、一个为0,结果为1
例如,2|3 = 3
2 的原码为: 00000000 00000000 00000000 00000010
3 的原码为: 00000000 00000000 00000000 00000011
2^3 原码为: 00000000 00000000 00000000 00000001 = 1
4. 按位取反 ~:0变成1、1变成0
例如,~2 = -3
对2的原码取反:11111111 11111111 11111111 11111101 (取反后结果的补码,也就是-3的补码。我们需要从补码推出原码,才能得到-3)
转换成反码: 11111111 11111111 11111111 11111100 (补码减1)
转换成原码: 10000000 00000000 00000000 00000011 =-3 (符号为不变,其他位取反)
简单应用
1. 位运算交换
a=a^b;
b=a^b;
a=a^b;
2. 消除x的最后一位的1
x & (x-1)
x = 1100
x-1 = 1011
x & (x-1) = 1000
//Given [1,2,2,1,3,4,3], return 4
//因为只有一个数恰好出现一个,剩下的都出现过两次,所以只要将所有的数异或起来,就可以得到唯一的那个数。
int a[7]={1,2,2,1,3,4,3};
int ans=0;
for(int i=0;i<7;i++){
ans^=a[i];
}
System.out.printf(ans);
总结
-
正数的原码、反码、补码都一样;
-
负数的反码 = 原码的符号位不变,其他位取反;
-
负数的补码 = 反码+1;
-
0的原码、反码、补码都是0;
-
计算机以补码进行运算;
-
取反不同于反码;