数字电路实验-带有减法功能的加法器

一、加法器

一位加法器就是异或作为结果,与作为进位标志,因此可以很简单的设计出一位加法器:

如果将四个一位加法器连起来,那么就成 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
2
3
assign subs = ~B;
assign {Carry,Result} = A + subs + 1;
assign Overflow = (A[n-1] == subs[n-1]) && (Result [n-1] != A[n-1]);

对于 0-(-8):

  • subs = 0111
  • {Carray, Result} = 0000 + 0111 + 1 = 0_1000
  • Overflow = (0 == 0) && (1 != 0) = 1

能正确计算溢出标志。

方法二

1
2
3
assign subs = ~B + 1;
assign {Carry,Result} = A + subs
assign Overflow = (A[n-1] == subs[n-1]) && (Result [n-1] != A[n-1]);

对于 0-(-8):

  • subs = 1000
  • {Carray, Result} = 0000 + 0111 + 1 = 0_1000
  • Overflow = (0 == 1) && (1 != 0) = 0

没能计算出溢出标志。

为什么会这样?那是因为将补码再求补码时,求补码的目的为了计算它的相反数,任何一个除了 0 的数的相反数,符号位一定相反,但是对于最小的负数比如-8 来说,它的相反数会溢出,从7 溢出到了-8,因此还是自身。但不管怎样,这仍然属于溢出。为了统一,我们将求补码延迟,这样能准确抓到被减数的符号位,这很关键。


数字电路实验-带有减法功能的加法器
http://blog.luliang.online/2024/07/15/数字电路基础实验3/
作者
Luyoung
发布于
2024年7月15日
许可协议