【问题标题】:Wrapping my head around hardware representations of numbers: a hypothetical two's complement question围绕数字的硬件表示来思考:一个假设的二进制补码问题
【发布时间】:2011-05-02 01:52:15
【问题描述】:

这是一个超级幼稚的问题(我知道),但我认为这将是一个很好的起点,可以考虑如何实际执行 CPU 的基本指令集:

在二进制补码系统中,您不能反转您的实现可以表示的最大负数的符号。这样做的理论原因很明显,最大负数的否定将超出实现的范围(范围总是类似于
-128 到 127)。

但是,当您尝试对最负数执行否定运算时实际发生的情况非常奇怪。例如,在 8 位表示中,最大负数是 -128,或二进制的 1000 0000。通常,要否定一个数字,您会翻转所有位,然后加一个。但是,如果您尝试使用 -128 执行此操作,您最终会得到:

1000 0000 ->
0111 1111 ->
1000 0000

与您开始时使用的号码相同。为此,wikipedia 称其为“奇怪的数字”。

在同一篇wikipedia 文章中,它说上述否定

被检测为溢出条件,因为最高有效位进位但没有进位。

所以我的问题是:
A) 这到底是什么意思?和
B) 似乎 CPU 每次执行基本算术运算时都需要执行额外的错误检查步骤,以避免与此否定相关的事故,从而产生大量开销。如果是这种情况,为什么不截断可以表示的数字范围以将奇怪的数字排除在外(即 8 位的 -127 到 127)?如果不是这样,您如何在不产生额外开销的情况下实施此类错误检查?

【问题讨论】:

  • 虽然处理器检测到溢出,但它不会自动对其作出反应。有汇编指令可用于事后对其进行测试。我不知道有任何更高级别的语言以任何方式使用它。

标签: language-agnostic hardware math twos-complement instruction-set


【解决方案1】:

MSB 的进位位用作标志,表示我们 需要更多位。没有它,我们将拥有一个modular arithmetic1 的系统,而无法检测我们何时回绕。

在模算术中,您处理的不是数字,而是 equivalence classes 具有相同余数的数字。在这样的 一个系统,将 1 加到 127 之后,你会得到 -128,你会 得出 +128 和 -128 属于同一个等价类的结论。

如果您将自己限制在 -127 到 +127 范围内的数字,您 必须重新定义加法,因为 127 + 1 = -127 是无意义的。

计算机向您呈现补码运算时, 本质上是具有检测溢出能力的模运算。

这是将0001 添加到 4 位加法器时的样子 0111。您可以看到,在 MSB 中,进位和进位是 不同:

     0        0        0        1
     | 0      | 1      | 1      | 1
     | |      | |      | |      | |
     v v      v v      v v      v v
0 <- ADD <-1- ADD <-1- ADD <-1- ADD <- 0
^     |    ^   |        |        |
      v        v        v        v
      1        0        0        0

ALU 使用这个标志来表示发生了溢出, 无需任何额外步骤。

1.模运算从 0 到 255 而不是 -127 到 128,但基本思想是相同的。

【讨论】:

  • 喜欢这个图表。非常有帮助。我想我现在明白了。我还有一个问题:尽管您已经阐明了为什么它是完全必要的,但是添加一个检查 MSB 的电路会以任何方式减慢 ALU 的速度吗?我试图了解 ALU(或 CPU 或其他)中的电路与执行任务需要多长时间之间的确切关系。
  • @tel Real ALU 实际上有一个专用层来计算进位位,而不是我在这里使用的波纹进位。要实际检查溢出,由软件检查状态寄存器(没有中断或任何东西)。基本上,您是在询问将 ALU 的状态存储到控制寄存器是否需要额外的时间。我认为不会,因为它可以与指令的其余部分同时发生。然而,根据维基百科,the first MIPS chip 的设计者却不这么认为。
  • 这是模运算的事实也意味着加法和乘法不依赖于有符号或无符号的数字,作为位模式的结果是相同的。
【解决方案2】:

不是 CPU 进行另一次检查,而是安排 晶体管 在发生这种情况时进行通知。它们是这样构建的,因为工程师在开始设计之前就选择了二补码。

结果是它发生在与返回非溢出结果相同的时钟周期内。


它是如何工作的?

“加1”阶段实现级联逻辑:从LSB开始,每个位依次接受真值表

old-bit  carry-in  new-bit  carry-out
-------------------------------------
   0        0        0         0
   0        1        1         0
   1        0        1         0
   1        1        0         1

(即new-bit = old-bit xor carry-incarry-out = old-bit and carry-in)。 LSB 的“进位”是我们要添加的 1,而对于其余位,它是前一个位的“进位”(这就是为什么必须在级联中完成) .

这些电路中的最后一个只是为signed-overflow = (carry-in and not carry-out) 添加了一个电路。

【讨论】:

  • 那么(避免电路图,如果可能的话)该检查的实际逻辑是如何工作的?
  • 那个真值表很棒,在老派的意义上。你对这些东西了解得太多了。那么 MSB 的附加电路是否会减慢处理器的速度呢?我试图了解 ALU(或 CPU 或其他)中的电路与执行任务需要多长时间之间的确切关系。
  • 特定操作的链中的每个步骤都需要有限的时间。芯片上的某处是一组用于某些操作的电路,该操作需要单个时钟的最后一个皮秒。如果评估有符号溢出时的and 使该操作成为限制性操作,那么您可以争辩说它“减慢”了芯片速度。但我会为甜甜圈提供美元,但无论你想命名什么芯片,它都不是。
  • @dmckee:虽然它可能在另一种意义上“减慢”芯片?额外的路径会带来额外的热开销,进而影响您可以安全地为芯片计时的速度。当然,芯片的时钟是整数,所以我希望它是(例如)百万分之一芯片的 1MHz 减速而不是每个芯片 1Hz 减速的机会。这假设了一个制造过程,其中芯片在生产线上进行测试,并且时钟与单个单元可以处理的一样快。芯片制造是一种概率游戏,我声称自己并不真正了解。
  • @史蒂夫也许。这里描述的版本是介绍性的教科书版本,如果人行道有任何阻尼器,我将超出我的深度。看起来像dwelch is the expert answerer here
【解决方案3】:

首先,维基百科文章指出它不能从负符号数否定为有符号数。它们的意思是因为它需要 9 位来表示正 128,而您无法使用 8 位寄存器来做到这一点。如果您要从负符号转换为正无符号转换,那么您有足够的位。当你否定 0x80 时,硬件应该给你 0x80,因为这是正确的答案。

二进制补码中的加法、减法、乘法等加法与小学的十进制数学没有什么不同。您排列二进制数,添加列,该列的结果是最低有效位,其余的将转移到下一列。因此,例如将 0b001 添加到 0b001

1 001 001 === 010

最右列两个1相加,结果为0b10(十进制2),写0后进位一,一加零加零为一,无进位,零加零为零,结果为0b010 .

1 加 1 的最右边一列是 0b10,我们写 0 进位一,那么进位一同时是最右边一列的进位,也是第二列的进位。此外,对于纸笔数学,我们通常只在非零时谈论进位,但如果你仔细想想,你总是会携带一个数字,就像我们的第二列一样,一加零就是一个零。

您可以将二进制补码否定视为反转并加一,或者将位遍历并反转到一个点然后不反转,或者将零的结果减去数字。

您可以使用铅笔和纸在二进制中进行减法计算,但与十进制相比,借用时会让人头疼,但可以。对于您所问的问题,请考虑反转并添加一个。

如果你把它减少到比 8 更少的位,你会更容易理解这一点,3 是一个易于管理的数字,它都是从那里扩展的。

所以下面的第一列是输入,第二列是反转版本,第三列是第二列加一。第四列是 msbit 的进位,第五列是 msbit 的进位。

000 111 000 1 1 001 110 111 0 0 010 101 110 0 0 011 100 101 0 0 100 011 100 1 0 101 010 011 0 0 110 001 010 0 0 111 000 001 0 0

快速了解添加一到两位:

00+1 = 001 01+1 = 010 10+1 = 011 11+1 = 100

对于将数字加一的情况,从第二位执行到第三位的唯一情况是当您的位全为 1 时,其中的单个零会停止级联进位位。因此,在上面的三位反转表中,只有两种情况下您有进位到 msbit 是 111 和 011,因为这是仅有的两种情况下所有低位都已设置。对于 111 情况,msbit 有一个进位和一个进位。对于 011 情况,msbit 有进位但没有进位。

所以正如其他人所说,芯片中连接了晶体管,如果设置了 msbit 进位并且未设置 msbit 进位,则在某处设置一些标志,否则清除标志。

所以请注意,上面的三位示例是按比例缩放的。如果在反转之后并且在添加一个之前,您有 0b01111111 那么您将在没有进位的情况下获得进位。如果您有 0b11111111,那么您将获得进位和进位。请注意,零也是一个数字,当您反转它时,您会得到相同的数字,不同之处在于当这些位被认为是有符号时,在取反时可以表示零,而全零则不能表示 1。

但底线是,这不是危机或世界末日的事情,处理器中有大量的数学和其他操作,其中进位位和有效位从一侧或另一侧脱落并溢出和下溢正在触发,等等。大多数情况下,程序员从不检查这种情况,而这些位只是掉在地板上,有时会导致程序崩溃,或者有时程序员使用 16 位数字进行 8 位数学运算只是为了确保没有什么不好的事情发生,或者出于同样的原因将 8 位数字用于 5 位数学。

请注意,硬件不知道有符号或无符号的加法和减法。硬件也不知道如何减去。硬件加法器是三位加法器(两个操作数和进位),结果和进位。将这些中的 8 根连线,您有一个 8 位加法器或减法器,无进位相加是两个操作数直接连接一个零作为 lsbit 进位。加进位是两个操作数直接连接到连接到lsbit 进位。减法是与第二个操作数反转并在进位位上加一个。至少从高层次的角度来看,这些逻辑都可以得到优化和实现,通常有两种在随意检查时很难理解的方式。

真正有趣的练习是乘法,想想用铅笔和纸做二进制乘法,然后意识到它比十进制容易得多,因为它只是一系列的移位和加法。给定足够的门,您可以将每个结果位表示为一个方程,方程的输入是操作数。这意味着如果您愿意,您可以进行单时钟倍频,在早期,门太多,所以完成了多时钟移位和加法,今天我们烧掉了门并获得单时钟倍频。另请注意,理解这也意味着如果您说 16 位 = 8 位乘以 8 位乘法,则无论是有符号乘法还是无符号乘法,低 8 位结果都是相同的。因为大多数人都会做 int = int * int; 这样的事情。如果您只关心结果位(不检查标志等),您真的不需要有符号或无符号乘法。有趣的东西..

【讨论】:

    【解决方案4】:

    在 ARM 架构手册 (DDI100E) 中:

    OverflowFrom
         Returns 1 if the addition or subtraction specified as its parameter 
         caused a 32-bit signed overflow. [...]
         Subtraction causes an overflow if the operands have different signs,
         and the first operand and the result have different signs.
    
    NEG
         [...]
         V Flag = OverflowFrom(0 - Rm)
    

    NEG 是用于计算一个数的否定的指令,即二进制补码。

    V 标志表示有符号溢出,可用于条件分支。它在不同的处理器架构中是相当标准的,连同其他三个标志 Z(零)、C(进位)和 N(负)。

    对于0 - (-128) = 0 + 128 = -128,第一个操作数是0,第二个操作数和结果都是-128,所以满足溢出条件,设置V标志。

    【讨论】:

      猜你喜欢
      • 2021-04-20
      • 1970-01-01
      • 2011-12-18
      • 1970-01-01
      • 2018-02-19
      • 1970-01-01
      • 2010-10-22
      • 2021-12-21
      • 1970-01-01
      相关资源
      最近更新 更多