Zhu.Yang

朱阳的个人博客(公众号:think123)

0%

计算机中的补码

二进制表示法

在计算机的世界里,只有0和1,我们也通常使用0和1来表示数字,比如8在计算机(8位机)中的二进制表示法就是00001000,那么负数呢?在计算机中如何表示负数呢?

大学学习过的计算机原理中告诉我们负数在计算机中是使用补码(维基百科中也叫作二补数)进行表示的,那么介绍这种表示方法之前想要介绍几个概念。

原码:将一个整数,转换成二进制,就是其原码。如单字节的5的原码为:0000 0101;-5的原码为1000 0101

反码:正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。如单字节的5的反码为:0000 0101;-5的原码为1111 1010。

补码:正数的补码就是其原码;负数的反码+1就是补码。如单字节的5的补码为:0000 0101;-5的补码为1111 1011。

1
2
3
4
5
System.out.println(Integer.toBinaryString(-5));
System.out.println(Integer.toBinaryString(5));
//在我的计算机中的输出结果为:
-5的二进制为:11111111 11111111 11111111 11111011
5的二进制为: 101

为什么要使用补码表示负数?
假设我们不使用补码,而仍然采用高位标示符号位的方法,那么-5我们可以表示为1000 0101,那么我们来做一个加法。看看8+(-5)=3是否可以正确实现。

1
2
3
4
0 0 0 0 1 0 0 0
1 0 0 0 0 1 0 1
------------------
1 0 0 0 1 1 0 1

最终的结果是-13,很明显这个结果并不是我们想要的,难道需要为正数和负数相加设计一套新的电路?

现在我们来看看使用补码的方式,同样运行8+(-5)=3

1
2
3
4
5
6
  0 0 0 0 1 0 0 0
1 1 1 1 1 0 1 1
------------------
0 0 0 0 0 0 0 1 1 此时产生了溢出位,将被自动舍弃。

[1000 0101(-5源码)------>1111 1010(-5反码,除符号位之外取反)----->1111 1011(补码)]

此时,得到的就是我们想要的结果3了。

补码的本质

在回答补码为什么能正确实现加法运算之前,我们先看看它的本质,也就是那两个步骤的转换方法是怎么来的。
要将正数转成对应的负数,其实只要用0减去这个数就可以了。比如,-8其实就是0-8。
已知8的二进制是00001000,-8就可以用下面的式子求出:

1
2
3
 0 0 0 0 0 0 0 0 
0 0 0 00 0 0
---------

因为00000000(被减数)小于0000100(减数),所以不够减。请回忆一下小学算术,如果被减数的某一位小于减数,我们怎么办?很简单,问上一位借1就可以了。

所以,0000000也问上一位借了1,也就是说,被减数其实是100000000,算式也就改写成:

1
2
3
4
1 0 0 0 0 0 0 0 0 
0 0 0 0 1 0 0 0
---------
 1 1 1 1 1 0 0 0

进一步观察,可以发现100000000 = 11111111 + 1,所以上面的式子可以拆成两个:

1
2
3
4
5
6
7
 1 1 1 1 1 1 1 1
0 0 0 0 1 0 0 0
---------
 1 1 1 1 0 1 1 1
0 0 0 0 0 0 0 1
---------
 1 1 1 1 1 0 0 0

2的补码的两个转换步骤就是这么来的。

为什么正数加法适用补码?

实际上,我们要证明的是,X-Y或X+(-Y)可以用X加上Y的2的补码完成。
Y的2的补码等于(11111111-Y)+1。所以,X加上Y的2的补码,就等于X + (11111111-Y) + 1,我们假定这个算式的结果等于Z,即 Z = X + (11111111-Y) + 1

那么Z = X - Y + 100000000 = X-Y(100000000在8位机中产生了溢出),这就证明了可以使用补码来进行负数的加法运算

关于补码的其他说明

  1. 二进制补码
  2. 二补数–维基百科

欢迎关注我的其它发布渠道