计算机中的位运算(以Java为例)
最近在学Java基础,遇到位运算,顺便写了篇文章,顺手发了出来,也让自己多了一分理解吧
一、前置知识
- 当我们谈论一个二进制数的时候,首先要明确它的位数,比如二进制:11111000,如果是一个16位数(或更大),则表示的十进制数是:248;如果是一个8位数,则表示的十进制数为-8
- 二进制的最高位是符号位:0表示正数,1表示负数
- 计算机运算的时候用的是补码,我们人看一个数的时候,原码是最方便我们理解的
- 正数的原码,反码,补码都一样(三码合一)
- 负数的反码=除符号位外,其它位取反
- 负数的补码=它的反码+1
- 0的反码、补码都是0
二、位运算符(以Java为例)
- &:按位与:相对应的位,两个都为1,结果为1,否则为0
- |:按位或:相对应的位,两个中有一个为1,结果为1,否则为0
- ^:按位异或:相对应的位,两个不一样,结果为1,否则为0
- ~:按位取反:相应位,0变1,1变0
- >>:算术右移:低位溢出,高位补符号位
- <<:算术左移:高位溢出,低位补0
- >>>:无符号右移:低位溢出,高位补0
注意:Java中没有无符号左移
三、举例说明
以下都以32位数为例
如果运算结果首位为1,则为负数,需要转化成原码之后我们才能看出来
如果运算结果首位为0,则为正数,不需要转换
1.&:按位与
@Test
public void test01() {
/*
&规则:相对应的位,两个都为1则为1,否则为0
3 & 5
3 00000000000000000000000000000011
5 00000000000000000000000000000101
3 & 5 运算结果:
三码合一
00000000000000000000000000000001 => 1
*/
System.out.println("3 & 5 = " + (3 & 5));
/*
3 & -5
3 00000000000000000000000000000011
-5 11111111111111111111111111111011
3 & -5 运算结果:
三码合一
00000000000000000000000000000011 => 3
*/
System.out.println("3 & -5 = " + (3 & -5));
/*
-3 & -5
-3 11111111111111111111111111111101
-5 11111111111111111111111111111011
-3 & -5 运算结果:
补码 11111111111111111111111111111001
反码 11111111111111111111111111111000
原码 10000000000000000000000000000111 => -7
*/
System.out.println("-3 & -5 = " + (-3 & -5));
}
输出结果:
3 & 5 = 1
3 & -5 = 3
-3 & -5 = -7
2. |:按位或
@Test
public void test02() {
/*
| 规则:相对应的位,两个有一个为1则为1,否则为0
3 | 5
3 00000000000000000000000000000011
5 00000000000000000000000000000101
3 | 5 运算结果:
三码合一
00000000000000000000000000000111 => 7
*/
System.out.println("3 | 5 = " + (3 | 5));
/*
3 | -5
3 00000000000000000000000000000011
-5 11111111111111111111111111111011
3 | -5 运算结果:
补码 11111111111111111111111111111011
反码 11111111111111111111111111111010
原码 10000000000000000000000000000101 => -5
*/
System.out.println("3 | -5 = " + (3 | -5));
/*
-3 | -5
-3 11111111111111111111111111111101
-5 11111111111111111111111111111011
-3 | -5 运算结果:
补码 11111111111111111111111111111111 (这里可以看出是-1)
反码 11111111111111111111111111111110
原码 10000000000000000000000000000001 => -1
*/
System.out.println("-3 | -5 = " + (-3 | -5));
}
输出结果:
3 | 5 = 7
3 | -5 = -5
-3 | -5 = -1
3.^:按位异或
@Test
public void test03() {
/*
^ 规则:相对应的位,不同为1,相同为0
3 ^ 5
3 00000000000000000000000000000011
5 00000000000000000000000000000101
^运算结果:
三码合一
00000000000000000000000000000110 => 6
*/
System.out.println("3 ^ 5 = " + (3 ^ 5));
/*
3 ^ -5
3 00000000000000000000000000000011
-5 11111111111111111111111111111011
^运算结果:
补码 11111111111111111111111111111000
反码 11111111111111111111111111110111
原码 10000000000000000000000000001000 => -8
*/
System.out.println("3 ^ -5 = " + (3 ^ -5));
/*
-3 ^ -5
-3 11111111111111111111111111111101
-5 11111111111111111111111111111011
^运算结果:
三码合一
00000000000000000000000000000110 => 6
*/
System.out.println("-3 ^ -5 = " + (-3 ^ -5));
}
输出结果:
3 ^ 5 = 6
3 ^ -5 = -8
-3 ^ -5 = 6
4.~:按位取反
@Test
public void test04() {
/*
~ 规则,相应的位,1变0,0变1
~3
3 00000000000000000000000000000011
~运算结果:
补码 11111111111111111111111111111100
反码 11111111111111111111111111111011
原码 10000000000000000000000000000100 => -4
*/
System.out.println("~3 = " + (~3));
/*
~-5
-5 11111111111111111111111111111011
~运算结果:
三码合一
00000000000000000000000000000100 => 4
*/
System.out.println("~-5 = " + (~-5));
/*
顺便提一下,这里有个规律:~3+1=-3,~-3+1=3
但是此规则不适用于下边界
*/
System.out.println("~3 + 1 = " + (~3 + 1));
System.out.println("~-3 + 1 = " + (~-3 + 1));
}
输出结果:
~3 = -4
~-5 = 4
~3 + 1 = -3
~-3 + 1 = 3
5.>>:算术右移
@Test
public void test05() {
/*
>> 规则:低位溢出,高位补符号位
在一定范围内,相当于右移1位就除以2,如果是奇数,则相当于加1除以2
如果超出这个范围,原数是正数,则最终得到的值是0;原数是负数,则最终得到的值是-1
32 00000000000000000000000000100000
32 >> 2 运算结果:
三码合一
00000000000000000000000000001000 => 8 相当于32除以(2^2)
32 >> 6 运算结果:
三码合一
00000000000000000000000000000000 => 0 只要位数超过5,结果都为0
*/
System.out.println("32 >> 2 = " + (32 >> 2));
System.out.println("32 >> 5 = " + (32 >> 5));
System.out.println("32 >> 6 = " + (32 >> 6));
/*
-32 >> 2
-32 11111111111111111111111111100000
-32 >> 2 运算结果:
补码 11111111111111111111111111111000
反码 11111111111111111111111111110111
原码 10000000000000000000000000001000 => -8 相当于-32除以(2^2)
-32 >> 5 运算结果:
补码 11111111111111111111111111111111 只要位数大于等于5,结果都是这个,即-1
反码 11111111111111111111111111111110
原码 10000000000000000000000000000001 => -1
*/
System.out.println("-32 >> 2 = " + (-32 >> 2));
System.out.println("-32 >> 5 = " + (-32 >> 5));
System.out.println("-32 >> 6 = " + (-32 >> 6));
}
输出结果:
32 >> 2 = 8
32 >> 5 = 1
32 >> 6 = 0
-32 >> 2 = -8
-32 >> 5 = -1
-32 >> 6 = -1
6.<<:算术左移
@Test
public void test06() {
/*
<< 规则:高位溢出,低位补0
在一定范围内,相当于左移1位就乘以2
如果超出这个范围,则最终得到的值会是0
32 00000000000000000000000000100000
32 << 2 运算结果:
三码合一
00000000000000000000000010000000 => 128 相当于32乘以(2^2)
32 << 25 运算结果:
三码合一
01000000000000000000000000000000 => 1073741824 相当于32乘以(2^25)
32 << 26 运算结果:
补码: 10000000000000000000000000000000 => 刚好是int的最小范围 -2147483648
32 << 27 运算结果:
00000000000000000000000000000000 => 0
*/
System.out.println("32 << 2 = " + (32 << 2));
System.out.println("32 << 25 = " + (32 << 25));
System.out.println("32 << 26 = " + (32 << 26));
System.out.println("Integer.MIN_VALUE="+Integer.MIN_VALUE);
System.out.println("32 << 27 = " + (32 << 27));
System.out.println("32 << 28 = " + (32 << 28));
/*
-32 11111111111111111111111111100000
-32 << 2 运算结果:
补码: 11111111111111111111111110000000
反码: 11111111111111111111111101111111
原码: 10000000000000000000000010000000 => -128 相当于-32乘以(2^2)
-32 << 26 运算结果:
补码: 10000000000000000000000000000000 => 刚好是int的最小范围 -2147483648 相当于32乘以(2^25)
-32 << 27 运算结果:
00000000000000000000000000000000 => 0
*/
System.out.println("-32 << 2 = " + (-32 << 2));
System.out.println("-32 << 26 = " + (-32 << 26));
System.out.println("-32 << 27 = " + (-32 << 27));
System.out.println("-32 << 28 = " + (-32 << 28));
}
输出结果:
32 << 2 = 128
32 << 25 = 1073741824
32 << 26 = -2147483648
Integer.MIN_VALUE=-2147483648
32 << 27 = 0
32 << 28 = 0
-32 << 2 = -128
-32 << 26 = -2147483648
-32 << 27 = 0
-32 << 28 = 0
7.>>>:无符号右移
@Test
@Test
public void test07() {
/*
>>> 规则:低位溢出,高位补0,
Java中的处理方式:对于int类型的数而言,是移动 %32 位,也就是说 >>>2于 >>>34是一样的
32 >>> 2
32 00000000000000000000000000100000
32 >>> 2 运算结果:
三码合一
00000000000000000000000000001000 => 8 相当于32除以(2^2)
32 >>> 6 运算结果:
三码合一
00000000000000000000000000000000 => 0 只要 %32之后,位数超过5,结果都为0
*/
System.out.println("32 >>> 2 = " + (32 >>> 2));
System.out.println("32 >>> 34 = " + (32 >>> 34));
System.out.println("32 >>> 5 = " + (32 >>> 5));
System.out.println("32 >>> 6 = " + (32 >>> 6));
/*
-32 >>> 2
-32 11111111111111111111111111100000
-32 >>> 2 运算结果:
补码 00111111111111111111111111111000
反码 00111111111111111111111111110111
原码 01000000000000000000000000001000 => 1073741816
-32 >>> 31 运算结果:
三码合一
00000000000000000000000000000001 => 1
-32 >>> 32 (-32 >>> 0)运算结果:-32
-32 >>> 34 (-32 >>> 2)
*/
System.out.println("-32 >>> 2 = " + (-32 >>> 2));
System.out.println("-32 >>> 31 = " + (-32 >>> 31));
System.out.println("-32 >>> 32 = " + (-32 >>> 32));
System.out.println("-32 >>> 34 = " + (-32 >>> 34));
}
输出结果:
32 >>> 2 = 8
32 >>> 34 = 8
32 >>> 5 = 1
32 >>> 6 = 0
-32 >>> 2 = 1073741816
-32 >>> 31 = 1
-32 >>> 32 = -32
-32 >>> 34 = 1073741816
如有不妥之处,请不吝指出,万分感谢!