ZhouChuang's Blog ZhouChuang's Blog
首页
  • 常用命令
  • 日常总结
  • Prometheus
  • 基础
  • 常用库
专题
  • 「编码」
更多
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Zhou Chuang

为学日益 为道日损
首页
  • 常用命令
  • 日常总结
  • Prometheus
  • 基础
  • 常用库
专题
  • 「编码」
更多
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 编码-读书笔记

    • 逻辑门和加法器
    • 如何实现减法
      • 十进制减法如何不涉及借位
        • 减数大于被减数
      • 二进制减法如何不涉及借位
        • 减数大于被减数
      • 改造加法器来实现减法
      • 二进制如何表示负数
  • 基础
  • 编码-读书笔记
ZhouChuang
2025-02-13
目录

如何实现减法

本文内容是《编码:隐匿在计算机软硬件背后的语言》的第13章的内容的总结。主要讲述了计算机系统如何实现减法的这个问题。

加法和减法在某些方面相互补充,但是在机制方面这两个运算则是不同的。加法是始终从两个加数的最右列向最左列进行运算,每一列的进位加到下一列中。在减法中没有进位,而有借位(这是一种与加法存在本质区别的麻烦机制)。

# 十进制减法如何不涉及借位

我们来看一个经典的借位减法:

image-20250214000952673

我们用下面的小技巧让减法不涉及借位。

  1. 为了避免借位,首先要从 999 减去减数

    image-20250214001136750

    由于操作数是三位,所以这里使用 999,如果操作数是 4 位,就用 9999。从一串 9 中减去一个数叫做对9求补数。176 对 9 求补数是 823。反之亦然:823 对 9 的补数是 176。这样做的好处是不需要借位。

  2. 计算出减数对 9 对补数后,将补数与原来对被减数相加。

    image-20250214001731357
  3. 最后再将结果加 1 ,并减去 1000 。

    image-20250214001830625

这样就得到最终的结果了,并且过程没有用到借位。

为什么这种方法能行得通?

253−176

等价于

253−176+1000−1000∴253−176+999+1−1000

组合一下:

253+(999−176)+1−1000

这个式子与刚才描述过的计算方式是相同的。我们用两个减法和两个加法来替代一个减法,而在这个过程中避免了烦琐的错位。

# 减数大于被减数

如果减数大于被减数会怎么样呢?

例如下面的这个问题:

image-20250217224410406

你可能会想到,交换两个数,算出结果然后加上负号。其实这样做也会遇到借位问题。

那么如何不使用借位而求解这个问题呢?这就需要采用与之前稍微不同的方法。

  1. 使用 999 减去 253,计算出对 9 的补数:

    image-20250217224741223
  2. 把该数对 9 的补数与被减数相加:

    image-20250217224839196

​ 按照前面的例子,下一步应该先加 1,并减去1000得到最终结果。但是这里并不适用。因为会遇到 923 减去 1000 的情况,也会遇到借位。

  1. 这里直接减去 999:

    image-20250217225123483
  2. 在这里我们再交换减数和被减数,用 999 减去 922,这样就不会使用到借位,其结果也与我们的期望相同:等于 -77

​

# 二进制减法如何不涉及借位

同样的技巧可以用于二进制数中,而且实际上比十进制数简单。

原题目:

image-20250217225620148

将这些数字转化为二进制数,问题将变为:

image-20250217225847966
  1. 用11111111(即255)减去减数:

    image-20250217225952336

    再十进制数减法时,减数是从一串 9 中减去的,结果被称为 9 的补数。在二进制中,减数是从一串 1 中减去的,其结果称为 1 的补数。

    但是在求对 1 的补数时其实并没有用到减法:只需要将 1 变为 0, 0 变为 1 即可。因此对 1 求补数有时也会称为相反数或反码。

  2. 将减数对 1 的补数与被减数相加:

    image-20250217230605162
  3. 将上式所得结果加 1:

    image-20250217230724951
  4. 减去 100000000(即256):

    image-20250217230837921

其结果就等于十进制的 77。

# 减数大于被减数

我们把这个数颠倒位置再做一遍。在十进制中,减法题目对应于:

image-20250217224410406

而用二进制表示为:

image-20250217231103312
  1. 用 11111111 减去减数,得到对 1 的补数:

    image-20250217231205455
  2. 将减数对 1 的补数与被减数相加:

    image-20250217231311555

    在被减数大于减数的时候,我们使用先加 1 再减 100000000 来完成计算。

  3. 而被减数小于减数的时候。我们先用 11111111 减去上面的结果:

    image-20250217231706514

    这里我们得到的这个结果是 77,而真正想要的结果应该是 -77。 这是我们一会要解决的问题。

# 改造加法器来实现减法

这里为了不让问题太复杂,这个新的加/减法器只执行在减数小于被减数的减法操作。即结果为正数的情况。

这个加法器的核心是由逻辑门集成的 8 位全加器。

8位全加器

这里输入 A0~A7 和 B0 ~ B7 与两排分别表示两个要相加的 8 位二进制的开关相连。进位输入接地。S0~S7 与表示结果的 8 个灯泡相连。由于这个加法有可能得到 9 位数值,所以进位输入也连接了一个灯泡。

控制面板如图所示:

image-20250217233045055

8 位的加减法器所用的新面板较从前做了些许改动。它增设了一个开关,用于选择做加法还是做减法。

image-20250217233312920

如上图所示,这个开关向下👇表示加法,向上👆表示减法。此外,8 个灯泡表示结果。最左侧的灯泡表示上溢和下溢。上溢(overflow)表示加法中得到了大于 255 。下溢(underflow)表示减法出现了负数。

在加法器上新增的主要部分就是一个用来求 8 位二进制数对 1 补数的电路。之前提到,二进制对 1 求补数相当于对其每位取反,因此我们马上想到计算 8 位二进制数补数的时候可以简单地应用 8 个反向器。

但是如果我们既想做加法又想做减法,就需要当且仅当进行减法运算时才实现反转。电路可以改造为如下图所示:

image-20250217234220124

这里标记为取反的信号将会被输入到每个异或门中。异或门的工作方式如下所示:

异或门的真值表

因此,如果 “取反“ 信号为 0,则 8 个异或门的输出与输入相同。如果 “取反” 信号为1,则输出信号将会反置。

将 8 个异或门合并的这个器件,称为求补器,如下图所示:

求补器

将一个求补器,一个 8 位二进制加法器和一个异或门做如下连接:

加减法器

⚠️注意:这里三个信号都标识位 SUB,这就是加/减法转换开关。当该信号为 0 的时候,其进行的是加法运算,反之为 1 的时候,则为减法运算。

在执行减法中,输入B送入加法器之前,需要先通过求补电路进行取反。此外,我们通过设定 CI(进位输入)为 1 来使得结果加 1。而在加法中,求补电路不起作用,且输入 CI 为 0。

加法器的 SUB 信号 和 CO(进位输出)输出作为异或门的输入来控制表示上溢/下溢的灯泡💡。如果 SUB 信号为 0(表示进行加法运算),则当加法器 CO 输出为 1 是灯泡亮,意为加法计算结果大于 255(上溢)。

当进行减法操作的时候,如果减数(输入B)小于被减数(输入A),这时加法器的 CO 端应该输入 1,表示在减法的最后一步中要减去 100000000。而此时SUB也为1,则上溢/下溢灯泡不亮。所以,只有当加法器的 CO 端输出为 0 时,上溢/下溢指示灯才会亮。这表示减数大于被减数,结果为负数。

这样一个加减法器的框架就搭建完成了。

# 二进制如何表示负数

应用二进制的目的在于只希望用 0 和 1 来表示所有的东西,当然也包括负号。

当然,可以简单的用一个二进制位来表示负号,当这一位为 1 时,表示负数,为 0 时表示正数,尽管这样可行,但是还远远不够。比如如何方便负数和正数相加呢?

看下面的数字:

image-20250218001829532

我们可以用如下的表示方法来不用负号表示负数:

image-20250218002000870

注意,这样就形成了一个循环排序。最小的负数(500)看起来像是最大的正数(499)的延续。而数字 999(实际上是-1)是比 0 小 1 的第一个负数。如果将 999 加 1 ,会得到 1000 。由于我们处理三位数,那个 1 被舍弃,这个结果实际上就是 000 。

这种标记方法称为 10 的补数。为了将三位负数转化为 10 的补数,我们用 999 减去它再加 1 。例如想要得到 -255 对 10 的补数,用 999 减去 255 得到 744,然后再加 1,得到 745。

利用这种补数,我们将不会在用到减法。所有的步骤都用加法来进行。例如 2 - 1,就可用 002 + 999 = 001 就表示 1。

这样的机制在二进制中被称为 2 的补数。以 8 位二进制数为例。范围为 00000000 ~ 11111111,对应这十进制中的 0 ~ 255。但是如果还想表示负数的话,则以 1 开头的每个 8 位数都表示一个负数,如下表所示:

image-20250218003253815

现在所表示的范围是 -128 ~ +127。最左位作为符号位(sign bit)。在符号位中 1 表示负数,0 表示正数。

如何计算 2 的补数:每位取反再加一。例如,十进制的 125 用二进制表示为 01111101,先将 01111101 的每位按位取反,得到 10000010 ,再加 1,得到 10000011 。其实如果在用同样的步骤,每位取反再加 1 ,就可以将数值还原。

这个系统为我们提供了一种不用负号就能表示正负数的方法。同样我们可以自由的将正数和负数用加法法则相加。

⚠️注意的是,这个系统可能涉及了上溢和下溢的情况。即结果大于 127 或者 小于 -128。

笔记

现在,二进制数可以由两种不同的使用方法。可以是有符号的,也可以是无符号的。

#计算机组成原理#数字电路
上次更新: 2025/05/06, 13:52:56
逻辑门和加法器

← 逻辑门和加法器

最近更新
01
find 命令介绍
05-06
02
交换分区的配置
04-09
03
top 命令介绍
04-08
更多文章>
Theme by Vdoing | Copyright © 2019-2025 Zhou Chuang 版权所有| 鲁ICP备2021031629号-2
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式