Java位运算原理及解析

1,058 阅读3分钟

Don't forget, a person's greatest emotional need is to feel appreciated.

莫忘记,一个人最大的情感需求是受到赏识。

1 Java支持位运算的数据类型

byte、short、int、long、char

2、原码、反码、补码基本概念

  • 原码:一个整数按照绝对值大小转换成的二进制数称为原码。
  • 反码:将二进制按位取反,所得的新二进制数称为原二进制数的反码。
  • 补码:反码加1称为补码。

3 位运算操作符

  • &:按位与。
  • |:按位或。
  • ~:按位非。
  • ^:按位异或。
  • <<:左移运算符。
  • >> :右移运算符。
  • <<<:无符号右移运算符。

位运算符中,除~以外,其余均为二元运算符。操作数只能为整型和字符型数据。

Java中使用补码来表示二进制数,在补码中最高位表示符号位,整数为0,负数为1。补码的规则如下:

  • 对于正数而言,最高位为0,其他各位表示数值本身(以二进制表示),例如:+42,二进制表示为(00000000 00000000 00000000 00101010)。
  • 对于负数而言,最高位为1,把该数绝对值的补码按位取反然后加1,即得到该数的补码。例如:-1,二进制表示为(11111111 11111111 11111111 11111111)。
    • 第一步:-1取绝对值为 1。
    • 第二步:1的补码为 00000000 00000000 00000000 00000001。
    • 第三步:1的补码按位取反 11111111 11111111 11111111 11111110。
    • 第四步:1的补码按位取反后加1得 11111111 11111111 11111111 11111111。

3.1 按位与(&)

按位与的运算规则:全1则为1,有0则为0。

A: 0 1 0 0 1 0 1 1
B: 1 1 0 1 0 1 0 1
与: 0 1 0 0 0 0 0 1 

3.2 按位或(|)

按位或的运算规则:有1则为1,全0则为0。

A: 0 1 0 0 1 0 1 1
B: 1 1 0 1 0 1 0 1
与: 1 1 0 1 1 1 1 1 

3.3 按位非(~)

按位取非,0变1,1变0。

5取非:
5: 00000101
结果: 11111010 = -6

10取非:
10: 00001010
结果: 11110101 = -11

0 取非:
0: 00000000 00000000 00000000 00000000
结果: 11111111 11111111 11111111 11111111 = -1

-1 取非:
-1: 11111111 11111111 11111111 11111111
结果: 0

-2 取非:
-2: 11111111 11111111 11111111 11111110
结果: 1

总结: 一个数取非的结果是,(当前数+1)*-1

3.4 按位异或(^)

按位异或规则:异或的两位不同则为1,相同则为0。

5^7=2
5: 00000101
7: 00000111
异或结果: 00000010 = 2

5^0=5
5: 00000101
0: 00000000
异或结果: 00000101 = 5

任何数与0做异或,结果还是数本身。

1^1=-2
-1: 11111111 11111111 11111111 11111111
1: 00000000 00000000 00000000 00000001
异或结果: 11111111 11111111 11111111 11111110 = -2

10^1=11
10: 00001010
1: 00000001
异或结果: 00001011 = 11

总结:

  1. a ^ a = 0
  2. a ^ b = b ^ a
  3. a ^b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;
  4. d = a ^b ^ c 可以推出 a = d ^ b ^ c.
  5. a ^ b ^a = b.

3.5 左位移(<<)

符号位不变,低位补0。

  • 数值value向左移动num位,左边二进制位丢弃,右边补0。(注意byte和short类型移位运算时会变成int型,结果要强制转换)
  • 若左移后,最高位是1,则变成负数。
  • 左移时舍弃位不包含1,则左移一位,相当于num乘2。
1<<31 = -2147483648
1: 00000000 00000000 00000000 00000001
1左移31位: 10000000 00000000 00000000 00000000

3<<2 = 12
3: 00000000 00000000 00000000 00000011
3左移2位: 00000000 00000000 00000000 00001100

3.6 右位移(>>)

  • 数值value向右移动num位,正数左补0,负数左补1,右边舍弃。(即保留符号位)
  • 右移一次,相当于除以2,并舍弃余数。
15右移2位:15>>2 = 3
15二进制: 00000000 00000000 00000000 00001111
右移2位: 00000000 00000000 00000000 00000011

-3右移2位:-3>>2 = -1
-3二进制: 11111111 11111111 11111111 11111101
右移2位: 11111111 11111111 11111111 11111111

3.7 无符号右位移(>>>)

  • 左边位用0补充,右边丢弃。
-3无符号右位移2位: -3>>>2 = 1073741823
-3二进制: 11111111 11111111 11111111 11111101
无符号右移2位: 00111111 11111111 11111111 11111111

4 常见应用

4.1 交换两个数的值

不使用临时变量,交换两个变量的数值。

int a = 2;
int b = 3;
a = a ^ b;
b = a ^ b; // b=(a^b)^b=a^0=a
a = a ^ b; // a=(a^b)^a=b^0=b
System.out.println("a=" + a + ", b=" + b);// a=3, b=2

4.2 判断一个数的奇偶性

n & 1 == 1 "奇数" ? "偶数"

4.3 取绝对值

(a^(a>>31))-(a>>31)

先整理一下使用位运算取绝对值的思路:若a为正数,则不变,需要用异或0保持的特点;若a为负数,则其补码为源码翻转每一位后+1,先求其源码,补码-1后再翻转每一位,此时需要使用异或1具有翻转的特点。

任何正数右移31后只剩符号位0,最终结果为0,任何负数右移31后也只剩符号位1,溢出的31位截断,空出的31位补符号位1,最终结果为-1.右移31操作可以取得任何整数的符号位。

那么综合上面的步骤,可得到公式。a>>31取得a的符号,若a为正数,a>>31等于0,a^0=a,不变;若a为负数,a>>31等于-1 ,a^-1翻转每一位。

5 Java中进制转换

public void convert() {
	// 十进制 -> 十六进制
	System.out.println(Integer.toHexString(num)); // a
	// 十进制 -> 八进制
	System.out.println(Integer.toOctalString(num)); // 12
	// 十进制 -> 二进制
	System.out.println(Integer.toBinaryString(num)); // 1010

	// 十六进制 -> 十进制
	System.out.println(Integer.parseInt("a", 16)); // 10
	// 八进制 -> 十进制
	System.out.println(Integer.parseInt("12", 8)); // 10
	// 二进制 -> 十进制
	System.out.println(Integer.parseInt("1010", 2)); // 10
}