如何表示负数,才能让加法器支持负数运算?

上节,我们对加法器进行优化,让它支持减法运算。那么,有办法让它也支持 负数 运算吗?

开始探讨解决方案之前,我们先研究:如何用二进制位来表示负数?常见方法有以下三种:

  • 原码true form
  • 反码 ,又称为 一的补码1’s complement
  • 补码 ,又称为 二的补码2’s complement

原码

跟正数一样,负数也需要转成二进制,保存在比特位中,负号也不例外。因此,我们可以将最左边的比特位拿来做符号位,0 表示正数,1 表示负数。以 8 位二进制整数为例,

  • 最高位(第 7 位,也是最左位)表示符号;
  • 7 位(第 0~6 位)保存绝对值;

原码是人脑最容易理解和计算的表示方式,但不利于设计运算电路,因此极少使用。至于为什么很难设计原码负数运算电路,可以自己思考一下。

首先,减法运算可以转化成加法运算,减负数等价于加上对应正数,减正数等价于加上对应负数。我们做带负数的加法运算时,需要先对操作数取绝对值,并对比大小。根据操作数符号和绝对值大小,决定做加法或减法运算。结果的符号也是由绝对值大的操作数决定。

想要实现这样的运算电路,不容易吧……

反码

反码表示法也是用第一位来表示符号,其余部分保存绝对值。不同的是,负数先对绝对值求一的补数(又称为反码)后再保存:

您可能会好奇,为什么负数绝对值要先求反码后再保存呢?

请看上图,正数和对应的负数绝对值加起来等于一串 1 ,这有利于运算电路的设计。不过反码还不是最便于电路设计的表示形式,而是即将粉墨登场的——补码。

补码

我们的目标很明确:以最低的成本,最好是零成本,让上节改进过的加法器进一步支持负数运算。那这是否意味着,我们可以采用反向推理的思路,来寻找问题的答案呢?

我们知道,一个整数 a ,加上它的负数 -a ,结果等于零:

$a + (-a) = 0$

请思考一下,如果加法器 A 端输入 aB 端输入一个什么数 x ,才能使得加法器输出的结果 S0 呢(忽略进位输出)?我们只要找到 x ,就找到负数 -a 的最佳表示法!

我们刚学过反码表示法,整数和对应的负数相加,结果等于一串 1 。而一串 1 再加上 1 ,结果就会变成一串 0 ,并且产生一个进位输出。如果将产生的进位忽略,加法器便支持负数运算了!

是的!我们已经找到了负数的最佳表示形式——在反码的基础上加一:

请看上图,试试将整数和对应的负数相加,得到的结果刚好为零!虽然会产生进位输出,但我们可以将其忽略。这就是计算机采用补码来表示负数的原因,处理器运算电路大大简化了!

实际上,我们还可以通过另一个思路来反推补码表示法。众所周知,负数等于零减去对应的正数:

$-a = 0 - a$

而上节中的加法器已经支持减法,用它来计算 0 和正数 a 的差,会产生溢出错误 F 。如果将溢出错误忽略,将结果看做负数 -a ,加法器也就很自然的支持了负数运算了!

那么,此时加法器输出什么呢?由于输入端 A0 ,因此结果为输入端 B 和进位输入 CI 之和。而输入端 B 又是 a 取反而来,CI 则等于 1 ,因此输出的结果就是 $\overline{a}+1$ ,其中 $\overline{a}$ 表示 a 取反。

由此,我们再次得到负数的最佳表示法:负数等于对应正数取反后再加一。这样一来,一个正数 a 与对应的负数相加,结果为 $a + \overline{a} + 1$ ,等于一串 1 再加上 1 ,最终结果等于 1 后面带着一串 0

换句话讲,负数其实是用 10000… 减去对应正数来表示的,其中 0 的个数由位数决定。我们以 8 位为例,-10 可以这样表示:

$100000000_{(256)} - 00001010_{(10)} = 11110110_{补码}$

这也是补码又称为 二的补数 的原因,100000000 最前面的 1 代表 2 的幂。而反码是和一串 11111111 的差值,则被称为 一的补数 。是的,一的补数中的一,指的就是 11111111

$11111111_{(255)} - 00001010_{(10)} = 11110101_{反码}$

最后,我们以 8 个二进制位为例,来考察一下补码的表示范围。众所周知,8 位无符号二进制数可以表示的范围是从 0255 ,列举如下表:

二进制位 十进制(无符号)
00000000 0
00000001 1
00000010 2
11111101 253
11111110 254
11111111 255

8 个位用来表示有符号整数,正数要减半,因为另一半要用来表示负数:

二进制位 十进制(有符号)
00000000 0
00000001 1
00000010 2
01111101 125
01111110 126
01111111 127
10000000 -128
10000001 -127
10000010 -126
11111101 -3
11111110 -2
11111111 -1

因此,8 位有符号二进制数可以表示的范围是从 -128127

这种表示方式,虽然对我们来说并不直观,但对计算机来说却非常友好!举个例子,1 加上 -1 ,等价于 $00000001+11111111 = 100000000$ ,忽略最前面的进位,刚好就是结果零!

小菜自制计算机】系列文章首发于公众号【小菜学编程】,敬请关注:

【自制计算机】系列文章首发于公众号【小菜学编程】,敬请关注: