7.[Java 基础]介绍四种进制与位运算


1. 进制介绍

进制介绍

进制就是进位制,是人们规定的一种进位方法。 对于任何一种进制—X进制,就表示某一位置上的数运算时是逢X进一位,二进制就是逢二进一,八进制是逢八进一,十进制是逢十进一,十六进制是逢十六进一。

Java进制分为二进制,八进制,十进制,十六进制, 但是计算机只能处理2进制的数据和指令。

  • 对于整数, 有四种表示方式:
进制 组成 规则 开头
二进制 0,1 满 2 进 1 0b 或 0B 开头
八进制 0-7 满 8 进 1 0 开头
十进制 0-9 满 10 进 1
十六进制 0-9 A(10), B(11), C(12), D(13), E(14), F(15) 满 16 进 1 以 0x 或 0X 开头
  • 进制对照表
二进制 八进制 十进制 十六进制
0 0 0 0
1 1 1 1
10 2 2 2
11 3 3 3
100 4 4 4
101 5 5 5
110 6 6 6
111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 10 A
1011 13 11 B
1100 14 12 C
1101 15 13 D
1110 16 14 E
1111 17 15 F
10000 20 16 10
10001 21 17 11
  • 在代码中定义各种进制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class BinaryTest {
public static void main(String[] args) {

// 二进制
int i1 = 0b1010;
System.out.println("i1 = "+i1); // i1 = 10

// 八进制
int i2 = 01354;
System.out.println("i2 = "+i2); // i2 = 748

// 十进制
int i3 = 1234;
System.out.println("i3 = "+i3); // i3 = 1234

// 十六进制
int i4 = 0X7B8F;
System.out.println("i4 = "+i4); // i4 = 31631
}
}

2. 进制的转换

2.1 十进制转二进制

  • 规则

将十进制数除以 2, 直到最后商为 0 为止, 然后将最后一步得到的结果与之前每个步骤得到的余数倒过来, 就是对应的二进制数

  • 示例

将十进制数 35 转成对应的二进制数结果为 0B100011 计算过程如下图所示:

十进制转二进制手动计算过程

  • 验证
1
2
// 十进制转二进制
System.out.println(Integer.toBinaryString(35)); // 100011

2.2 十进制转八进制

  • 规则

将十进制数除以 8, 直到最后商为 0 为止, 然后将最后一步得到的结果与之前每个步骤得到的余数倒过来, 就是对应的二进制数

  • 示例

将十进制数 156 转成八进制数为 0234 计算过程如下图所示:

十进制转八进制

  • 验证
1
2
// 十进制转八进制
System.out.println(Integer.toOctalString(156)); // 234

2.3 十进制转十六进制

  • 规则

将十进制数除以 16, 直到最后商为 0 为止, 然后将最后一步得到的结果与之前每个步骤得到的余数倒过来, 就是对应的二进制数

  • 示例

将十进制数 2021 转成 十六 进制数为 7E5 计算过程如下图所示:

十进制转十六进制

  • 验证
1
2
// 十进制转十六进制
System.out.println(Integer.toHexString(2021)); // 7e5

2.4 二进制转十进制

  • 计算规则

    从最右侧开始, 将每个位上的数提出来, 乘以 2 的 (位数-1) 次方, 然后将这些数求和

  • 示例

将二进制数 0B1011 转成十进制数为 11 计算过程如下表格所示:

位数 4 3 2 1
二进制数 1 0 1 1
逐位计算 $1*(2*^{(4-1)})$ $0*(2*^{(3-1)})$ $1*(2*^{(2-1)})$ $1*(2*^{(1-1)})$
简化幂数 $1*(2*^3)$ $0*(2*^2)$ $1*(2*^1)$ $1*(2*^0)$
简化算式 1*8 0*4 1*2 1*1
逐位结果 8 0 2 1
最终结果 8+0+2+1 = 11
  • 验证
1
2
// 二进制转十进制
System.out.println(0B1011); // 11

2.5 八进制转十进制

  • 计算规则

    从最右侧开始, 将每个位上的数提出来, 乘以 8 的 (位数-1) 次方, 然后将这些数求和

  • 示例

将八进制数 01034 转成十进制为 540 计算过程如下表格所示:

位数 4 3 2 1
八进制数 1 0 3 4
逐位计算 $1*(8*^{(4-1)})$ $0*(8*^{(3-1)})$ $3*(8*^{(2-1)})$ $4*(8*^{(1-1)})$
简化幂数 $1*(8*^{3})$ $0*(8*^{2})$ $3*(8*^{1})$ $4*(8*^{0})$
简化算式 1*512 0*64 3*8 4*1
逐位结果 512 0 24 4
最终结果 512+0+24+4 = 540
  • 验证
1
2
// 八进制转十进制
System.out.println(01034); // 540

2.6 十六进制转十进制

  • 计算规则

    从最右侧开始, 将每个位上的数提出来, 乘以 16 的 (位数-1) 次方, 然后将这些数求和.

  • 示例

将十六进制数 0XA1F9 转成十进制数为 41465 计算过程如下表格所示:

位数 4 3 2 1
十六进制数 A 1 F 9
对应十进制 10 1 15 9
逐位计算 $10*(16*^{(4-1)})$ $1*(16*^{(3-1)})$ $15*(16*^{(2-1)})$ $916^{(1-1)})$
简化幂数 $10*(16*^{3})$ $1*(16*^{2})$ $1516^{1})$ $9*(16*^{0})$
简化算式 10*4096 1*256 15*16 9*1
逐位结果 40960 256 240 9
最终结果 40960+256+240+9= 41465
  • 验证
1
2
// 十六进制转十进制
System.out.println(0XA1F9); // 41465

2.7 二进制转八进制

  • 规则

从低位开始, 将二进制数的每三个位为一组, 转成对应的十进制数, 然后将所有数拼接, 即为二进制对应的八进制

  • 示例

将二进制数 0B1011010111 转成八进制为 01327

二进制转八进制

  • 验证
1
2
// 二进制转八进制
System.out.println(Integer.toOctalString(0B1011010111)); // 01327

2.8 二进制转十六进制

  • 规则

从低位开始, 将二进制数的每四个位为一组, 转成对应的十进制数, 然后将所有数拼接, 即为二进制对应的八进制

  • 示例

将二进制数 0B1011010111 转成十六进制数为 0X2D7 , 计算过程如下图所示:

二进制转十六进制

  • 验证
1
2
// 二进制转十六进制
System.out.println(Integer.toHexString(0B1011010111)); // 2d7

2.9 八进制转二进制

  • 规则:

将八进制的每一位转成对应的三位二进制数,然后将所有二进制数拼接, 即为对应的二进制数值

  • 示例

将八进制数 0673 转成二进制数为 0B 1 1011 1011 , 计算过程如下图所示:

八进制转二进制

  • 验证
1
2
// 八进制转二进制
System.out.println(Integer.toBinaryString(0673)); // 110111011

2.10 十六进制转二进制

  • 规则:

将八进制的每一位转成对应的四位二进制数,然后将所有二进制数拼接, 即为对应的二进制数值

  • 示例

将十六进制数 0X9FA6 转成二进制数结果为 0B 1001 1111 1010 0110, 计算过程如下图所示:

十六进制转二进制

  • 验证
1
2
// 十六进制转二进制
System.out.println(Integer.toBinaryString(0X9FA6)); // 1001111110100110

3. 位运算

位运算时程序设计中的对二进制数进行的操作, 通常位运算比加减法略快, 但比乘除法运算要快很多, Java 支持 7 种位运算, 一般来说位运算只能操作整数类型的值或变量.

3.1 位运算符介绍

符号 名称 运算规则 规律
& 按位与 符号两侧全为 1, 结果为 1, 否则为0
| 按位或 符号两侧只要有一个为 1, 结果就为 1 (两侧全 0, 才为 0)
^ 按位异或 符号两侧一样为 0,不一样为 1
~ 按位取反
(单目操作)
0 变 1, 1 变 0
<< 算术左移 将操作数的二进制码整体左移指定位数,左移后右边空出来的位以0填充
>> 算术右移 把第一个操作数的二进制码右移指定位数
左边空出来的位以原来的符号位填充,即:
如果第一个操作数原来是正数,则左边补0;
如果第一个操作数原来是负数,则左边补1。
>>> 无符号右移 把第一个操作数的二进制码右移指定位数,左边空出来的位总是以0填充。

a b a&b a|b a^b ~a
0 0 0 0 0 1
0 1 0 1 1 1
1 0 0 1 1 0
1 1 1 1 0 0

3.2 原码、反码与补码

原码反码补码

  1. 二进制的最高位是符号位, 0 表示正数, 1 表示负数
  2. 正数的 原码, 反码, 补码 都一样
  3. 负数的 反码 = 原码的符号位不变,其他位取反
  4. 负数的 补码 = 反码 + 1
  5. 负数的 反码 = 补码 - 1
  6. 0 的 反码, 补码 都是 0
  7. Java 中的数都是有符号位的,没有无符号数
  8. 在计算机进行运算的时候, 都是以 补码 方式进行运算的,因为补码可以将正负数统一
  9. 当我们看结果的时候要看 原码

3.3 取反运算

  • 按位取反只需要一个操作数,
  • 按位取反将将操作数的二进制码包括符号位按位取反.
  • 操作符: ~
  • 结果规律: $~X = -X-1$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class BitwiseNot {
public static void main(String[] args) {

// 1. 计算 ~100
System.out.println("~100 = " + (~100)); // ~100 = -101

/**
* 解析
* 先得到 100 的源码 (正数 三码合一, 所以源码即反码即补码)
* 100 的源码为: 0000-0000 0000-0000 0000-0000 0110-0100
*
* 操作数的补码 -> 0000-0000 0000-0000 0000-0000 0110-0100 (执行取反运算 得到结果的补码)
* 结果数的补码 -> 1111-1111 1111-1111 1111-1111 1001-1011 (负数补码-1为反码 得到结果的反码)
* 结果数的反码 -> 1111-1111 1111-1111 1111-1111 1001-1010 (负数反码除符号位,其余取反得到结果的原码)
* 结果数的原码 -> 1000-0000 0000-0000 0000-0000 0110-0101 = > -101
*/

// 2. 计算 ~-100
System.out.println("~-100 = " + (~-100)); // ~-100 = 99

/**
* 解析
*
* 操作数的源码 -> 1000-0000 0000-0000 0000-0000 0110-0100 (得到反码, 除符号位所有位取反)
* 操作数的反码 -> 1111-1111 1111-1111 1111-1111 1001-1011 (得到操作数补码, 反码+1 = 补码)
* 操作数的补码 -> 1111-1111 1111-1111 1111-1111 1001-1100 (计算结果的补码,全部取反操作)
* 结果数的补码 -> 0000-0000 0000-0000 0000-0000 0110-0011 (结果为正数,三码合一, 所以这就是结果 99)
*/
System.out.println("~1000 = " + (~1000)); // ~1000 = -1001
System.out.println("~-1000 = " + (~-1000)); // ~-1000 = 999
System.out.println("~-569 = " + (~-569)); // ~-569 = 568
System.out.println("~-579 = " + (~-579)); // ~-579 = 578
System.out.println("~579 = " + (~579)); // ~579 = -580
}
}

 ~100 取反操作

~-100

3.4 按位与运算

按位与处理两个长度相同的二进制数,两个相应的二进位都为1,该位的结果值才为1,否则为0

  • 操作符: &
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class BitwiseAnd {
public static void main(String[] args) {

// 1. 计算 7 & 4
System.out.println("7 & 4 = " + (7 & 4)); // 7 & 4 = 4

/*
7 跟 4 都是正数, 均为 3 码合一
得到 7 的原码 0000-0000 0000-0000 0000-0000 0000-0111
得到 4 的原码 0000-0000 0000-0000 0000-0000 0000-0100
进行按位与运算----------------------------------------
得到结果 0000-0000 0000-0000 0000-0000 0000-0100
所以结果为 4
*/

// 2. 计算 -3 & 6
System.out.println("-3 & 6 = " + (-3 & 6)); // -3 & 6 = 4
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
1.
获得-3 的原码 1000-0000 0000-0000 0000-0000 0000-0011
获得-3 的反码 1111-1111 1111-1111 1111-1111 1111-1100
获得-3 的补码 1111-1111 1111-1111 1111-1111 1111-1101 (负数补码等于反码+1)
获得 6 的补码 0000-0000 0000-0000 0000-0000 0000-0110 (正数三码合一原码即补码)

1111-1111 1111-1111 1111-1111 1111-1101 (-3 的补码)
按位与 & 0000-0000 0000-0000 0000-0000 0000-0110 ( 6 的补码)
----------------------------------------------------
结果 0000-0000 0000-0000 0000-0000 0000-0100 (补码)
结果为正数 三码合一, 所以结果为 4
*/


// 3. 计算 -9 & -7
System.out.println("-9 & -7 = " + (-9 & -7)); // -9 & -7 = -15
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
负数的反码 = 原码除符号位取反

1. 得到 -9 的补码
-9 原码为: 1000-0000 0000-0000 0000-0000 0000-1001
-9 反码为: 1111-1111 1111-1111 1111-1111 1111-0110
-9 的补码: 1111-1111 1111-1111 1111-1111 1111-0111

2. 得到-7 的补码
-7 原码为: 1000-0000 0000-0000 0000-0000 0000-0111
-7 反码为: 1111-1111 1111-1111 1111-1111 1111-1000
-7 补码为: 1111-1111 1111-1111 1111-1111 1111-1001

3. 进行按位与运算
1111-1111 1111-1111 1111-1111 1111-0111 (-9 的补码)
& 1111-1111 1111-1111 1111-1111 1111-1001 (-7 的补码)
--------------------------------------------
结果的补码 1111-1111 1111-1111 1111-1111 1111-0001
转结果反码 1111-1111 1111-1111 1111-1111 1111-0000
转结果原码 1000-0000 0000-0000 0000-0000 0000-1111
所以结果为 -15
*/
}
}

 7 & 4 图示

 -3 & 6 图示

3.5 按位或运算

按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为1,该位的结果值为1

  • 操作符: |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class BitwiseOr {
public static void main(String[] args) {

// 1. 计算 8|5
System.out.println("8|5 = "+(8|5)); // 8|5 = 13
/*
8 跟 5 都是正数, 均为 3 码合一
得到 8 的原码 0000-0000 0000-0000 0000-0000 0000-1000(正数 原码=反码=补码)
得到 5 的原码 0000-0000 0000-0000 0000-0000 0000-0101(正数 原码=反码=补码)
进行按位与运算----------------------------------------
得到结果 0000-0000 0000-0000 0000-0000 0000-1101
所以结果为 13
*/


// 2. 计算 7|-6
System.out.println("7|-6 = "+(7|-6)); // 7|-6 = -1
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
1.
获得-6 的原码 1000-0000 0000-0000 0000-0000 0000-0110
获得-6 的反码 1111-1111 1111-1111 1111-1111 1111-1001
获得-6 的补码 1111-1111 1111-1111 1111-1111 1111-1010 (负数补码等于反码+1)
获得 7 的原码 0000-0000 0000-0000 0000-0000 0000-0111 (正数 原码=反码=补码)

1111-1111 1111-1111 1111-1111 1111-1010 (-6 的补码)
按位或 | 0000-0000 0000-0000 0000-0000 0000-0111 ( 7 的补码)
----------------------------------------------------
得到结果的补码 1111-1111 1111-1111 1111-1111 1111-1111
得到结果的反码 1111-1111 1111-1111 1111-1111 1111-1110
得到结果的原码 1000-0000 0000-0000 0000-0000 0000-0001
所以结果为 -1
*/

// 3. 计算 -5|-3
System.out.println("-5|-3 = "+(-5|-3)); // 7|-6 = -1
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
负数的反码 = 原码除符号位取反

1. 得到 -5 的补码
-5 原码为: 1000-0000 0000-0000 0000-0000 0000-0101
-5 反码为: 1111-1111 1111-1111 1111-1111 1111-1010
-5 的补码: 1111-1111 1111-1111 1111-1111 1111-1011

2. 得到-3 的补码
-3 原码为: 1000-0000 0000-0000 0000-0000 0000-0011
-3 反码为: 1111-1111 1111-1111 1111-1111 1111-1100
-3 补码为: 1111-1111 1111-1111 1111-1111 1111-1101

3. 进行按位与运算
1111-1111 1111-1111 1111-1111 1111-1011 (-5 的补码)
& 1111-1111 1111-1111 1111-1111 1111-1101 (-3 的补码)
--------------------------------------------
结果的补码 1111-1111 1111-1111 1111-1111 1111-1111
转结果反码 1111-1111 1111-1111 1111-1111 1111-1110
转结果原码 1000-0000 0000-0000 0000-0000 0000-0001
所以结果为 -1
*/
}
}

3.6 按位异或运算

  • 按位异或运算,操作的结果是如果不同则该位为1,相同否则该位为0
  • 操作符: ^
  • 说明:
    • 任何数与 0 做异或操作,结果都为其本身
    • 任何数与本身做异或操作,结果都为 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class BitwiseXor {
public static void main(String[] args) {
// 1. 计算 8^5

System.out.println("8^5 = "+(8^5)); // 8^5 = 13
/*
8 跟 5 都是正数, 均为 3 码合一
得到 8 的原码 0000-0000 0000-0000 0000-0000 0000-1000
得到 5 的原码 0000-0000 0000-0000 0000-0000 0000-0101
按位异或运算------------------------------------------
得到结果补码 0000-0000 0000-0000 0000-0000 0000-1101
所以结果为 13
*/

// 2. 计算 7^-6
System.out.println("7^-6 = "+(7^-6)); // 7^-6 = -3
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
1.
获得-6 的原码 1000-0000 0000-0000 0000-0000 0000-0110
获得-6 的反码 1111-1111 1111-1111 1111-1111 1111-1001
获得-6 的补码 1111-1111 1111-1111 1111-1111 1111-1010 (负数补码等于反码+1)
获得 7 的原码 0000-0000 0000-0000 0000-0000 0000-0111

1111-1111 1111-1111 1111-1111 1111-1010 (-6 的补码)
按位异或 ^ 0000-0000 0000-0000 0000-0000 0000-0111 ( 7 的补码)
----------------------------------------------------
得到结果的补码 1111-1111 1111-1111 1111-1111 1111-1101
得到结果的反码 1111-1111 1111-1111 1111-1111 1111-1100
得到结果的原码 1000-0000 0000-0000 0000-0000 0000-0011
所以结果为 -3
*/


// 3. 计算 -5^-3
System.out.println("-5^-3 = "+(-5^-3)); // -5^-3 = 6
/*
解析
负数的补码 = 反码 +1
负数的反码 = 补码 -1
负数的反码 = 原码除符号位取反

1. 得到 -5 的补码
-5 原码为: 1000-0000 0000-0000 0000-0000 0000-0101
-5 反码为: 1111-1111 1111-1111 1111-1111 1111-1010
-5 的补码: 1111-1111 1111-1111 1111-1111 1111-1011

2. 得到-3 的补码
-3 原码为: 1000-0000 0000-0000 0000-0000 0000-0011
-3 反码为: 1111-1111 1111-1111 1111-1111 1111-1100
-3 补码为: 1111-1111 1111-1111 1111-1111 1111-1101

3. 进行按位异或运算
1111-1111 1111-1111 1111-1111 1111-1011 (-5 的补码)
^ 1111-1111 1111-1111 1111-1111 1111-1101 (-3 的补码)
--------------------------------------------
结果的补码 0000-0000 0000-0000 0000-0000 0000-0110
正数三码合一,所以结果为 6
*/
}
}

3.7 算术左移运算

  • 算术左移是将二进制数整体左移指定位数
  • 左移后空出来的位用 0 补充
  • 操作符: <<
  • 规律 $x << n = x * ( 2^n )$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class BitwiseLeft {
public static void main(String[] args) {
// 1. 计算 4 << 2
System.out.println("4 << 2 = "+(4 << 2));
// = 4* (2^2) = 16
/*
得到 4 的补码 0000-0000 0000-0000 0000-0000 0000-0100
左移两位 00-0000 0000-0000 0000-0000 0000-0100-00
正数三码合一, 所以结果为 16
*/

// 2. 计算 6 << 3
System.out.println("6 << 3 = "+(6 << 3)); // 6 << 3 = 48

/*
得到 6 的补码 0000-0000 0000-0000 0000-0000 0000-0110
左移三位 0-0000 0000-0000 0000-0000 0000-0110-000
正数三码合一, 所以结果为 48
*/

// 3. 计算 -7 << 2
/*
1. 得到 -7 的补码
-7 的源码 1000-0000 0000-0000 0000-0000 0000-0111
-7 的反码 1111-1111 1111-1111 1111-1111 1111-1000
-7 的补码 1111-1111 1111-1111 1111-1111 1111-1001
左移两位 11-1111 1111-1111 1111-1111 1111-1001-00 (结果的补码)
结果的反码 11-1111 1111-1111 1111-1111 1111-1000-11
结果的原码 10-0000 0000-0000 0000-0000 0000-0111-00
所以结果为 -28
*/

System.out.println("-7 << 2 = "+(-7 << 2)); // -7 << 2 = -28
System.out.println("3 << 3 = "+(3 << 3)); // 3 << 3 = 24
}
}

算数左移算过程

3.8 算术右移运算

  • 算术右移是将二进制数整体右移指定位数,
  • 如果操作数是 正数 右移后空出来的位用 0 补充
  • 如果操作数是 负数 右移后空出来的位用 1 补充
  • 操作符: >>
  • 规律:$x >> n = x / ( 2^n )$
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BitwiseRight {
public static void main(String[] args) {
// 1. 计算 4 >> 2
System.out.println("4 >> 2 = "+(4 >> 2));
// = 4 / (2^2) = 1
/*
得到 4 的补码 0000-0000 0000-0000 0000-0000 0000-0100
右移两位 00-0000-0000 0000-0000 0000-0000 0000-01
正数三码合一, 所以结果为 1
*/

// 2. 计算 6 >> 3
System.out.println("6 >> 3 = "+(6 >> 3)); // 6 >> 3 = 48

/*
得到 6 的补码 0000-0000 0000-0000 0000-0000 0000-0110
右移三位 000 0000-0000 0000-0000 0000-0000 0000-0
正数三码合一, 所以结果为 0
*/

// 3. 计算 -7 >> 2
/*
1. 得到 -7 的补码
-7 的源码 1000-0000 0000-0000 0000-0000 0000-0111
-7 的反码 1111-1111 1111-1111 1111-1111 1111-1000
-7 的补码 1111-1111 1111-1111 1111-1111 1111-1001
右移两位 11-1111-1111 1111-1111 1111-1111 1111-10 (结果的补码)
结果的反码 11-1111-1111 1111-1111 1111-1111 1111-01
结果的原码 10-0000 0000-0000 0000-0000 0000-0000-10
所以结果为 -2
*/

System.out.println("-7 >> 2 = "+(-7 >> 2)); // -7 >> 2 = -2
System.out.println("-10 >> 2 = "+(-10 >> 2)); // -10 >> 2 = -3
System.out.println("-10 >> 3 = "+(-10 >> 3)); // -10 >> 3 = -2
System.out.println("-10 >> 4 = "+(-10 >> 4)); // -10 >> 4 = -1
System.out.println("3 >> 3 = "+(3 >> 3)); // 3 >> 3 = 0
}
}

算数右移计算过程

算数右移计算过程

3.9 无符号右移运算

  • 无符号右移是将二进制数整体右移指定位数, 右移后空出来的位用 0 补充
  • 操作符: >>>
  • 当操作数为正数的时候, 算术右移跟无符号右移相同位结果相同
  • 无符号右移运算结果永远是正数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class BitwiseRight2 {
public static void main(String[] args) {
// 1. 计算 4 >>> 2
System.out.println("4 >>> 2 = "+(4 >>> 2));
// = 4 / (2^2) = 1
/*
得到 4 的补码 0000-0000 0000-0000 0000-0000 0000-0100
右移两位 00-0000-0000 0000-0000 0000-0000 0000-01
正数三码合一, 所以结果为 1
*/

// 2. 计算 6 >>> 3
System.out.println("6 >>> 3 = "+(6 >>> 3)); // 6 >>> 3 = 48

/*
得到 6 的补码 0000-0000 0000-0000 0000-0000 0000-0110
右移三位 000-0000-0000 0000-0000 0000-0000 0000-0
正数三码合一, 所以结果为 0
*/

// 3. 计算 -7 >>> 4
/*
1. 得到 -7 的补码
-7 的源码 1000-0000 0000-0000 0000-0000 0000-0111
-7 的反码 1111-1111 1111-1111 1111-1111 1111-1000
-7 的补码 1111-1111 1111-1111 1111-1111 1111-1001
右移四位 0000-1111-1111 1111-1111 1111-1111 1111 (结果的补码)
正数三码合一, 所以结果为 268435455
*/

System.out.println("-7 >>> 4 = "+(-7 >>> 4)); // -7 >>> 2 = 268435455
System.out.println(0b1111111111111111111111111111); // 268435455

System.out.println("-10 >>> 2 = "+(-10 >>> 2)); // -10 >>> 2 = 1073741821
System.out.println("-10 >>> 3 = "+(-10 >>> 3)); // -10 >>> 3 = 536870910
System.out.println("-10 >>> 4 = "+(-10 >>> 4)); // -10 >>> 4 = 268435455

System.out.println("3 >>> 3 = "+(3 >>> 3)); // 3 >>> 3 = 0
}
}

无符号右移计算过程

4. 位运算的应用

4.1 判断奇偶数

只要根据数的最后一位是 0 还是 1 来决定即可,为 0 就是偶数,为 1 就是奇数。

1
2
3
4
5
6
int a = 10;
if(( a & 1 ) ==1){
System.out.println("奇数");
}else{
System.out.println("偶数");
}

已知:

奇数最后一位为1

偶数最后一位为0

& 操作 同 1 为 1,其余为 0

所以:

任何奇数与 1 进行 & 操作结果都为 1

任何偶数与 1 进行 & 操作结果都为 0

4.2 交换数值

1
2
3
4
5
6
7
8
// 交换 a,b 的值
public static void swap(int a,int b){
System.out.println("交换前:a="+a+", b="+b);
a ^= b;
b ^= a;
a ^= b;
System.out.println("交换后:a="+a+", b="+b);
}
  • 异或操作规律
    1. 相同为 0 不同为 1
    2. 任何数与 0 做异或操作,结果都为其本身
    3. 任何数与本身做异或操作结果都为0
  • 流程解析
  1. a ^= b

    同:a = ( a ^ b )

  2. b ^= a

    同:b = b ^ a

    根据步骤1 得出 a = a ^ b

    带入为:b = b ^ ( a ^ b )

    简化为:b = b ^ a ^ b

    简化为:b = b ^ b ^ a

    已知: 任何数与本身做异或操作结果都为0

    所以: b = 0 ^ a

    已知: 任何数与 0 做异或操作,结果都为其本身

    所以: b = a

  3. a ^= b

    同: a = a ^ b

    根据步骤2得出: b = b ^ a

    带入为:a = ( b ^ a ) ^ a

    简化为:a = b ^ a ^ a

    已知: 任何数与本身做异或操作结果都为0

    所以:a = b ^ 0

    已知: 任何数与 0 做异或操作,结果都为其本身

    所以: a = b

  • 使用位运算验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
a = 3;  b = 5;
i1 = 3 的原码 0011
i2 = 5 的原码 0101

# 1. 计算 a ^= b;
0011
^ 0101
-----------
0110

# a = 0110 = 6

# 2. 计算 b ^= a;
0110
^ 0101
--------------
0011

# b = 0011 = 3

# 3. 计算 a ^= b;
0011
^ 0110
-------------
0101
# a = 0101 = 5

4.3 实现加法运算

4.3.1 原理介绍

用位运算实现加法, 也就是计算机用二进制进行位运算计算加法,

首先我们来实现用1位数的加法,这里暂时不考虑进位(满2进1)的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1位2进制数,进行加法有四种情况, 即:
一位 二位 本位
1 + 1 = 0 # 这里为10,但是暂时不考虑进位,所以为0
1 + 0 = 1
0 + 1 = 1
0 + 0 = 0

# 可以用 异或运算符来代替
一位 二位 本位
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

上面进行了一位运算,下面进行两位计算, 两位计算问题在于“进位”, 即满2进1 的问题,

1
2
3
4
5
6
7
8
9
10
11
12
一位  二位  进位
1 + 1 = 1 # 结果为10 , 这里只看进位为1
1 + 0 = 0
0 + 1 = 0
0 + 0 = 0

这里可以使用逻辑与操作符来代替
一位 二位 进位
1 & 1 = 1 # 进位 结果为10
1 & 0 = 0 # 不进位
0 & 1 = 0 # 不进位
0 & 0 = 0 # 不进位

在位运算在中进位可以使用 逻辑左移运算符 “<<” 来实现,<< 1 也就是进1位

1
2
# 进位可以使用如下表达式
( a & b ) << 1

到这里, 有了两个基本的表达式,即:

1
2
3
4
5
# 加法操作
a ^ b

# 进位操作
( a & b ) << 1

做一下有两个位的2进制数的加法运算,在不考虑进位的情况下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 1. 算数加法计算 11 + 01 = 100
11
+ 01
--------
100

# 2. 用推算的表达式计算 11 + 01
# 前面推算的表达式为:
# 1 ^ 1 = 0
# 1 ^ 0 = 1
# 0 ^ 1 = 1
# 0 ^ 0 = 0

# 2.1 首先计算 11 ^ 01
11 ^ 01 = 10
11
^ 01
--------
10

# 2.2 进位
(11 & 01) << 1 = 10

11
& 01
------------
01 << 1 # 左移一位
------------
10

# 到这里, 我们用普通的加法去运算两个数的时候可以得到 10 + 10 = 100
# 但是我们不需要加法,所以要想别的方法, 如果让两个数按照刚才的方法计算一次呢?

# 2.3 计算 10 ^ 10
10 ^ 10 = 00
10
^ 10
-----------
00

# 2.4 进位
(10 & 10) << 1 = 100
10
^ 10
-----------
10 << 1
-----------
100

#到这里就基本得出了结论,其实后面那个 00 已经不需要再去计算了, 因为第一个表达式就已经算出了结果, 通过推理可以得出三位数的加法只需要重复的计算三次,得到第一个表达式的值就是计算
  • 结论
  1. 设 a,b 为两个二进制数,则 a + b = a ^ b + ( a & b ) << 1

    证明:a ^ b 时不考虑进位时加法结果。当二进制位同时为 1 时,才有进位,因此 ( a & b ) << 1 是进位产生的值,称为进位补偿。将两者相加便是完整加法结果。

  2. 使用 结论1 可以实现只用位运算进行加法运算。

    证明:利用 结论1 中的 等式 不停对自身进行迭代。每迭代一次,进位补偿右边就多一位0,因此最多需要加数二进制位长度次迭代,进位补偿就变为0,这时运算结束。

4.3.2 代码实现与解析

  • 通过代码使用位运算实现加法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public static void main(String[] args) {
add(11, 12);
}

private static int add(int a, int b) {
System.out.println("a = "+a+", b = "+b);
int sum = a;
while (b != 0) {
sum = a ^ b;
b = ( a & b ) << 1;
a = sum;
System.out.println("a= "+a+",b = "+b+", sum = "+sum);
}
System.out.println("结果 sum = "+sum);
return sum;
}
}

// 运算过程
a = 11 = 1011
b = 12 = 1100

1. 计算加法
sum = a ^ b
sum = 1011 ^ 1100
= 1011
^ 1100
--------
sum = 0111 = 7

2. 进位
b = (a & b) << 1
b = ( 1011 & 1100 ) << 1
1011
& 1100
----------------
1000 << 1
----------------
b = 10000 = 16


3. a = sum = 00111 = 7
4. b != 0 (true)

5. 计算加法
sum = a ^ b
sum = 00111 ^ 10000 = 10111
00111
^ 10000
--------------
sum = 10111 = 23

6. 进位
b = (a & b) << 1
b = ( 00111 & 10000 ) << 1
00111
& 10000
--------------
00000 << 1
--------------
b = 000000 = 0

7. a = sum = 10111

8. b != 0 (false) , 不进入循环,break;

9. 所以: sum = 10111 = 23

  • 逻辑解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    add(5,3)
private static int add(int a, int b) {
System.out.println("a = "+a+", b = "+b);
int sum = a;
while (b != 0) {
// 做加法运算
sum = a ^ b;
// 进位
b = ( a & b ) << 1;
a = sum;
System.out.println("a= "+a+",b = "+b+", sum = "+sum);
}
System.out.println("结果 sum = "+sum);
return sum;
}

a = 5, b = 3
a= 6,b = 2, sum = 6
a= 4,b = 4, sum = 4
a= 0,b = 8, sum = 0
a= 8,b = 0, sum = 8
结果 sum = 8

初始化数据 int a = 5; b = 3;

第一行: int sum = a;(int sum = 5)

第二行:while 循环 判断 b(3) !=0 为TRUE, 进入while 循环体

第三行(循环体第一行):sum = a ^ b(5 ^ 3)(计算加法1)

1
2
3
4
5
6
7
8
# 5 ^ 3 计算详情
# 5 = 0101(正数三码合一)
# 3 = 0011(正数三码合一)
0101
^ 0011
--------------
0110
# 所以: sum = 0110 (6)

第四行(循环体第二行):b = ( a & b ) << 1; (5 & 3)<< 1 (进位1)

1
2
3
4
5
6
7
8
9
10
11
# (5 & 3)<< 1 计算详情
# 5 = 0101
# 3 = 0011
# 1. 计算 5 & 3
0101
& 0011
--------------
0001
# 2. 进行 算数左移
0001 << 1 = 0010
#所以:b = 0010 (2)

第五行(循环体第三行): a = sum; (a= 0110 = 6)

第六行(循环体第四行):打印数据 a= 6, b = 2, sum = 6


第二行:while 循环 判断 b(2) !=0 为TRUE, 进入while 循环体

第三行(循环体第一行):sum = a ^ b(6 ^ 2)(计算加法2)

1
2
3
4
5
6
7
8
# 6 ^ 2 计算详情
# 6 = 0110(正数三码合一)
# 2 = 0010(正数三码合一)
0110
^ 0010
--------------
0100
# 所以: sum = 0100 (4)

第四行(循环体第二行):b = ( a & b ) << 1; (6 & 2)<< 1 (进位2)

1
2
3
4
5
6
7
8
9
10
11
# (5 & 3)<< 1 计算详情
# 6 = 0110(正数三码合一)
# 2 = 0010(正数三码合一)
# 1. 计算 6 & 2
0110
& 0010
--------------
0010
# 2. 进行 算数左移
0010 << 1 = 0100
#所以:b = 0100 (4)

第五行(循环体第三行): a = sum; (a= 0100 = 4)

第六行(循环体第四行):打印数据 a= 4, b = 4, sum = 4


第二行:while 循环 判断 b(4) !=0 为TRUE, 进入while 循环体

第三行(循环体第一行):sum = a ^ b(4 ^ 4)(计算加法3)

1
2
3
4
5
6
7
8
# 4 ^ 4 计算详情
# 4 = 0100(正数三码合一)
# 4 = 0100(正数三码合一)
0100
^ 0100
--------------
0000
# 所以: sum = 0000 (0)

第四行(循环体第二行):b = ( a & b ) << 1; (4 & 4)<< 1 (进位3)

1
2
3
4
5
6
7
8
9
10
11
# (4 & 4)<< 1 计算详情
# 4 = 0100(正数三码合一)
# 4 = 0100(正数三码合一)
# 1. 计算 6 & 2
0100
& 0100
--------------
0100
# 2. 进行 算数左移
0100 << 1 = 1000
#所以:b = 1000 (4)

第五行(循环体第三行): a = sum; (a= 0000 = 0)

第六行(循环体第四行):打印数据 a= 0, b = 8, sum = 0


第二行:while 循环 判断 b(8) !=0 为TRUE, 进入while 循环体

第三行(循环体第一行):sum = a ^ b(0 ^ 8)(计算加法4)

1
2
3
4
5
6
7
8
# 0 ^ 8 计算详情
# 0 = 0000(正数三码合一)
# 8 = 1000(正数三码合一)
0000
^ 1000
--------------
1000
# 所以: sum = 1000 (8)

第四行(循环体第二行):b = ( a & b ) << 1; (0 & 8)<< 1(进位4)

1
2
3
4
5
6
7
8
9
10
11
# (4 & 4)<< 1 计算详情
# 0 = 0000(正数三码合一)
# 8 = 1000(正数三码合一)
# 1. 计算 6 & 2
0000
& 1000
--------------
0000
# 2. 进行 算数左移
0000 << 1 = 0000
#所以:b = 0000 (0)

第五行(循环体第三行): a = sum; (a= 1000= 8)

第六行(循环体第四行):打印数据 a= 8, b = 0, sum = 8


第二行:while 循环 判断 b(0) !=0 为FALSE, 进入while 循环体

4.4 实现减法运算

已知 a - b = a + ( -b ) , 即取反后再用加法加起来即可

-b 即b 的相反数, 已知 ~X = -X-1 那么 ~X = (-X-1 )+1 即为 b 的相反数 即 ~X +1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class BinaryMinus {
public static void main(String[] args) {
System.out.println(minus(10,-5));
}

/**
* 做减法操作
*
* @param a
* @param b
* @return
*/
private static int minus(int a, int b) {
return add(a, negative(b));
}

/**
* 求相反数
*
* @param num
* @return
*/
private static int negative(int num) {
return add(~num, 1);
}

/**
* 做加法操作
*
* @param a
* @param b
* @return
*/
private static int add(int a, int b) {
System.out.println("a = " + a + ", b = " + b);
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
System.out.println("a= " + a + ",b = " + b + ", sum = " + sum);
}
System.out.println("结果 sum = " + sum);
return sum;
}
}

4.5 实现乘法运算

4.5.1 基本原理介绍

乘法的原理是通过加法计算,即a * b 就是将 b 个 a 相加。

我们先通过算数运算计算一下乘法运算 10 * 8

1
2
3
4
 10 * 8 = 80
变换1 : (10*2) * (8/2) = 20 * 4 = 80
变换2 : (20*2) * (4/2) = 40 * 2 = 80
变换3 : (40*2) * (2/2) = 80 * 1 = 80

这是最理想的情况,8 每次 除2 结果都是偶数,那当出现奇数的时候要怎么做呢?

1
2
3
10 * 6 = 60
变换1 = (10*2)*(6/2) = 20 * 3 = 60
变换2 = (20*2)*(3/2) = 40 * 1.5 = 60

可是在计算机中, 当我们使用int类型数据做 相除运算 时 可不会得出 1.5 这样的浮点数

所以这一步我们得到的结果应该为

1
= ( 20 * 2 ) * ( 3 / 2 ) = 40 * 1 = 40

这里的损失是1,即为前一步的20, 所以 40 + 20 = 60,

4.5.2 使用到的位运算符介绍

  • 将算数逻辑梳理完成之后, 我们再看一下位运算中几个用到的运算符
  1. 算数左移(<<)

算数左移, 每移动一位相当于 乘 2 的操作, 例如:

1
2
3
System.out.println(4<<1); // 8
System.out.println(5<<1); // 10
System.out.println(10<<1); // 20
  1. 无符号右移(>>>)

无符号右移每移动一位,相当于除以 2 ,例如:

1
2
3
System.out.println(4>>>1); // 2
System.out.println(8>>>1); // 4
System.out.println(10>>>1); // 5
  1. 异或操作 (^)

乘法操作:当两个数均为正数或均为负数时, 结果为正数,当一个整数一个负数时,结果为负数

异或运算:异或运算与陈发运算类似, 当两个数的符号位相同时,进行异或操作符号位结果为0,即正数, 当符号位不一样时,结果为1,即负数

4.5.3 实现代码与解析

  • 有了上面的介绍我们可以通过代码使用位运算实现乘法运算了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class BinaryMulti {
public static void main(String[] args) {
System.out.println(multi(5,-6));
}

private static int multi(int a, int b) {
// 将乘数 和 被乘数都取绝对值 
int multiplicand = a < 0 ? add(~a, 1) : a; // 被乘数
int multiplier = b < 0 ? add(~b, 1) : b; // 乘数

int res = 0;
// 判断 multiplier 任何数 * 0 = 0
while (multiplier != 0) {
//判断 multiplier 是不是 奇数
if ((multiplier & 1) != 0) {
// 如果是奇数 则加上一次multiplicand本身
res = add(res, multiplicand);
}
// multiplicand * 2
multiplicand <<= 1;
// multiplier / 2
multiplier >>>= 1;
}
// 计算乘积的符号  
if ((a ^ b) < 0) {
res = add(~res, 1);
}
return res;
}

// 加法计算
private static int add(int a, int b) {
System.out.println("a = " + a + ", b = " + b);
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
System.out.println("a= " + a + ",b = " + b + ", sum = " + sum);
}
System.out.println("结果 sum = " + sum);
return sum;
}
}
  • 就是当 multiplier 是偶数时,则每次让 multiplicand * 2 直到 multiplier / 2 = 0时。

  • 当b 是奇数时,先加上单独的那个 multiplicand,再重复 multiplier 为偶数的步骤。

  • 过程解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
计算  10 * 5 ,结果很显然等于 50

设置 multiplicand=10, multiplier = 5

1. multiplier 不为0, 进入 while 循环

2. 判断 multiplier(5) 是奇数 则执行 res = add(res, multiplicand); res = 10;

3. multiplicand << 1 = 10 << 1 = 10 * 2 = 20;

4. multiplier >>> 1 = 5 << 1 = 5 / 2 = 2;

5. multiplier 不为0, 进入 while 循环

6. 判断 multiplier 不为0, 判断 multiplier(2) 是偶数, 不进入 if 执行体

7. multiplicand << 1 = 20 << 1 = 20 *2 = 40;

8. multiplier>>>1 = 2 >>> 1 = 2 / 2 = 1;

9. multiplier 不为0, 进入 while 循环

10. 判断 multiplier(1) 是奇数 则执行 res = add(res, multiplicand); res = 50;

11. multiplicand << 1 = 40 << 1 = 40 *2 = 80;

12. multiplier>>>1 = 1 >>> 1 = 1 / 2 = 0;

13. multiplier 为0, 不进入 while 循环, 循环结束, 结果 res 为 50

4.6 实现除法运算

实现除法运算有两种思路:

  • 一种是使用减法运算

进行减法运算时, 不停的用除数减去被除数,知道除数小于被除数时,进行减法操作的次数就是商,此时被除数就是余数

这里还需要注意的就是商的符号,和余数的符号, 即他们是正数还是负数。商的符号确定方式跟乘法是一样的, 即除数被除数符号相同,则为正,如果除数与被除数符号不同,则为负,余数的符号和被除数的符号一致。计算流程和乘法类似, 我们需要先对两个数求绝对值,然后循环进行减法计算求商,然后求余数, 最后确定商与余数的符号。

  • 一种是进行乘法运算

除法则可以视为乘法的逆运算。对于二进制来说,从高到低的每一位,将除数提升到当前位的权值(即,乘以2^k,等同于左移k位),如果此时被除数扔大于除数,就说明结果在这个位上商1。然后从被除数减掉除数提升后的值。遍历每一位,即为最终的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
public class BinaryDivide {
public static void main(String[] args) {
divideByMinus(20, 6);
divideByMulti(20, 6);
}


/**
* 通过乘法的方式实现除法
* @param a
* @param b
* @return
*/
public static void divideByMulti(int a, int b) {
// 对被除数和除数取绝对值
int A = a < 0 ? add(~a, 1) : a;
int B = b < 0 ? add(~b, 1) : b;

int N = 0; // 商 N
for (int i = 31; i >= 0; i--) {
// 未使用A>=(B<<i)进行判断,因为只有左移B时舍弃的高位不包含1,才相当于该数乘以2的i次方.
if ((A >> i) >= B) { // A ÷ 2^i >= B
N += (1 << i); // N = N + 2^i
A -= (B << i); // A = A - B*2^i
}
}

int C = A; // 余数C
// 求商的符号
if ((a ^ b) < 0) {
N = add(~N, 1);
}
// 求余数的符号
if (a < 0) {
C = add(~C, 1);
}
System.out.println("商是"+ N+", 余数 = "+ C);
}

/**
* 通过减法的方式实现除法
* @param a
* @param b
* @return
*/
public static void divideByMinus(int a, int b) {
//对被除数和除数取绝对值
int A = a < 0 ? add(~a, 1) : a;
int B = b < 0 ? add(~b, 1) : b;
//对被除数和除数的绝对值求商
int C = A; // 余数 C
int N = 0; // 商 N
while (C >= B) {
C = minus(C, B); // 余数, C-B
N = add(N, 1); // 计算次数 N+1
}
// 求商的符号
if ((a ^ b) < 0) {
N = add(~N, 1);
}
// 求余数的符合
if (a < 0) {
C = add(~C, 1);
}
System.out.println("商是"+ N+", 余数 = "+ C);
}

// 减法运算
private static int minus(int a, int b) {
return add(a, negative(b));
}

/**
* 求相反数
*
* @param num
* @return
*/
private static int negative(int num) {
return add(~num, 1);
}

// 加法运算
private static int add(int a, int b) {
//System.out.println("a = " + a + ", b = " + b);
int sum = a;
while (b != 0) {
sum = a ^ b;
b = (a & b) << 1;
a = sum;
//System.out.println("a= " + a + ",b = " + b + ", sum = " + sum);
}
//System.out.println("结果 sum = " + sum);
return sum;
}
}

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录