关于'二补码'(Two's Complement)

前言

前一篇文章中,我提到了,在研究一个问题的时候,顺便了解了 Java 的 int 溢出处理。其实,在研究时,还涉及到了另一个概念的理解,那就是 Java 中负数的二进制表现形式:二补码(英文为 Two's Complement),在这篇文章中我们来了解一下。

正文

开始之前,我们先来回顾两个规则:

  • 在 Java 中,全部为有符号数,最高位为符号位,0代表正数,1代表负数
  • 在 Java 中 int 为 32位。

好,复习完了这两条规则,我们开始吧,我们先来说结论,在 Java 中,负数转正数或者正数转负数的操作都是用的 二补码 这个操作,这个操作内容就是:按位取反 + 二进制1,我们来看一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 十进制数
1640531527

// 1640531527 转为二进制
01100001110010001000011001000111

// 1640531527 按位取反
10011110001101110111100110111000

// 二进制的 1
00000000000000000000000000000001


// 二进制加法 1640531527 按位取反后的数 + 1
10011110001101110111100110111000
+ 00000000000000000000000000000001
-----------------------------------
= 10011110001101110111100110111001

// 最后的到的 10011110001101110111100110111001 从新转为 10 进制
-1640531527

上边的过程用 Java 代码表示:

1
System.out.println(~0b01100001110010001000011001000111 + 0b00000000000000000000000000000001);

关于负数正数,为什么是按位取反再加一这种规则,再多写一些,看了阮一峰的一篇文章,了解到的:

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
// 大家可以想一下,设 a = 1640531527,取取负数 b ,可以用 b = 0 - a 来实现,换成二进制运算如下

// 二进制的 0
00000000000000000000000000000000

// 二进制的 1640531527
01100001110010001000011001000111

// 二进制的减法 0 - 1640531527
00000000000000000000000000000000
- 01100001110010001000011001000111
-----------------------------------
=?

// 在上边的算式里,你会发现,没法减,下边的数比上边的大,想要减需要借位,也就是变成下边这样
100000000000000000000000000000000
- 01100001110010001000011001000111
-----------------------------------
= 10011110001101110111100110111000

// 上边的算式中,被减数是凭空接了 1 位的,这样就够减了,其实大家仔细观察一下,100000000000000000000000000000000 减 01100001110010001000011001000111 后得到的 10011110001101110111100110111000 实际上就是 01100001110010001000011001000111 按位取了一下反
// 我们再来观察一下 100000000000000000000000000000000 是可以按下面的式子分解的
100000000000000000000000000000000 = 11111111111111111111111111111111 + 00000000000000000000000000000001

// 那么,其实上边的减法我们可以化为如下形式
11111111111111111111111111111111
+ 00000000000000000000000000000001
-----------------------------------
= 100000000000000000000000000000000
- 01100001110010001000011001000111
-----------------------------------
= 10011110001101110111100110111000

// 再清晰一些:我们设 a = 01100001110010001000011001000111,求 -a,那么就是
-a = 0 - a
-a = (11111111111111111111111111111111 + 00000000000000000000000000000001) - a
-a = (11111111111111111111111111111111 - a) + 00000000000000000000000000000001

// 上边的式子就很清楚了 11111111111111111111111111111111 - a 其实就是把 a 按位取反,而后边加上了 00000000000000000000000000000001 就是 + 1,也就是正数转负数(负数转正数)就是按位取反再加一
-a = ~a + 1

参考资料

关于2的补码

2’s complement