数字电路实验-带有减法功能的加法器
一、加法器
一位加法器就是异或作为结果,与作为进位标志,因此可以很简单的设计出一位加法器:
如果将四个一位加法器连起来,那么就成 4位了串行进位加法器电路:
需要注意的是,上面的加法器是基础构建,它只执行加法功能,其它任何溢出、进位等等都是高层的概念,和加法器没有任何关系。
二、加法器执行加法计算
在数据类型一致的情况下,比如,长度统一。这时候就可以由加法器计算它们之间的加法运算,但是数据类型的长度注定了加法的结果会溢出、进位等行为。注意这是上层计算的范畴,和加法器本身 没有任何关系,我们是在讨论加法,而不是加法器。
1、溢出
溢出的概念是,两个相同符号的数比如两个负数或者正数相加后,结果超出了类型的范围。比如 带符号的 4 位类型,它的范围为{-8,….7},以下几种情况就会溢出:
- 3+6 = 9 > 7
- (-4)+(-5) = (-9) < -8
溢出后会怎么样呢?对于加法器来说,它并不知道什么是溢出。对于用补码表示的数,一旦溢出,符号位会反转。比如:
- 3+6 = 0011+0110 = 1001
- (-4)+(-5) = 1100+1011 = 0111
因此我们可以获得到这种溢出特征,那就是:assign Overflow = (A[n-1] == B[n-1]) && (Result [n-1] != A[n-1])。也就是说,两个操作数的符号必须相同,且结果符号翻转,这就是溢出。
需要注意的是,并不是说,这和加法器有关,这个和加法器半毛钱关系都没有,溢出能自动回绕这种特性,是人为设计出来的补码。
2、进位(借位)
什么时候会进位呢?溢出和进位等价吗?事实上,这两个概念没有任何关系:
- 3+6 = 0011+0110 = 1001
- (-4)+(-5) = 1100+1011 = 0111
显然第一个计算式并没有进位,但发生了溢出;第二个既有进位也有溢出。
在计算机运算中,溢出(overflow)、进位(carry)和借位(borrow)是处理数字运算时非常关键的概念,尤其在涉及二进制数的算术运算时。它们之间存在一定的联系,但也有重要的区别。
进位(Carry)
进位是指在二进制加法中,最高位(最左边的位)相加后产生的超出当前位数的额外位。进位仅在加法中产生,且通常用于无符号数的运算。例如,如果两个8位的无符号数相加,其和超过了255(即 (11111111_2)),则会产生一个向上的进位,但这个进位位于计算范围之外。
借位(Borrow)
借位是在二进制减法中使用的概念。当你从一个较小的位减去一个较大的位时,需要从更高的位“借”一位来完成减法。这是减法中的一个基本步骤,尤其是在多位数的减法中。
溢出(Overflow)
溢出是指在进行有符号数运算时,结果超出了该数据类型所能表示的范围。它通常与数的正负符号有关。溢出表示结果的符号位被错误地计算,从而导致结果不正确。例如,在一个8位的有符号整数系统中,最大值为127((01111111_2)),最小值为-128((10000000_2))。如果两个正数相加得到一个负数,或两个负数相加得到一个正数,这就发生了溢出。
关系和区别
- 关系:这三者都涉及二进制运算中位的额外调整。进位和借位直接影响相邻的高位或低位,而溢出则是对整个运算结果有效性的一个指标。
- 区别:
- 使用场景:进位用于加法中,借位用于减法中,而溢出可以在加法或减法中发生,尤其是涉及有符号数时。
- 影响范围:进位和借位影响的是单个位之间的运算,而溢出则是整个结果的有效性问题。
- 数据类型相关性:进位和借位通常与无符号数的处理更相关,溢出则主要关注有符号数。
因此,要判断进位标志,只需要:assign {Carry,Result} = A + B;
三、加法器执行减法运算
我们用的补码,补码的好处之一就是可以将减法表示为加法,操作前只需要将被减数写成相反数,然后加起来就行,比如:
- 3-2 = 0011-0010 = 0011+1110 = 0001
- (-2)-(-3) = 1110-1101 = 1110+0011 = 0001
那么为何要将 -B 再 求一次补码,给一个补码再补一次,这是在干什么?比如:
- -2 的补码为 [1110]补 = [0010] = 2;
- -3 的补码为 [1101]补 = [0011] = 3;
- 0 的补码为 [0000]补 = [0000] = 0;
- 7 的补码为 [0111]补 = [1001] = -7;
- -8 的补码为 [1000]补 = [1000] = -8;
- …
可以看到,补码的补码就在做一件事,那就是求它的相反数,这个相反数依然使用补码表示的。对于前几个,都能正常工作,但是对于 -8 问题就出来了,-8 的相反数就会溢出。
也就是说,在求特殊的最小的负数的补码时候,要注意溢出标志位的更新时机。比如:
- 0-(-8) = 0000-1000 = 0000+1000 = 1000
这种时机可以通过两种方法的不同来体现。
1、方法一
1 |
|
对于 0-(-8):
- subs = 0111
- {Carray, Result} = 0000 + 0111 + 1 = 0_1000
- Overflow = (0 == 0) && (1 != 0) = 1
能正确计算溢出标志。
方法二
1 |
|
对于 0-(-8):
- subs = 1000
- {Carray, Result} = 0000 + 0111 + 1 = 0_1000
- Overflow = (0 == 1) && (1 != 0) = 0
没能计算出溢出标志。
为什么会这样?那是因为将补码再求补码时,求补码的目的为了计算它的相反数,任何一个除了 0 的数的相反数,符号位一定相反,但是对于最小的负数比如-8 来说,它的相反数会溢出,从7 溢出到了-8,因此还是自身。但不管怎样,这仍然属于溢出。为了统一,我们将求补码延迟,这样能准确抓到被减数的符号位,这很关键。