【问题标题】:How is overflow detected at the binary level?如何在二进制级别检测溢出?
【发布时间】:2012-07-06 07:54:05
【问题描述】:

我正在阅读 Hennessey 和 Patterson 的《计算机组织与设计》教科书(第 4 版)。在第 225 页,他们描述了如何在有符号的 2 补码算术中检测到溢出。我什至无法理解他们在说什么。

“当它确实发生时,我们如何检测[溢出]?显然,添加或 减去两个 32 位数字可以产生需要 33 位的结果 充分表达。”

当然。而且它不需要 34 位,因为即使是最小的 34 位数字也是最小 33 位数字的两倍,我们正在添加 32 位数字。

"缺少第33位表示发生溢出时,符号位 用结果的 value 设置,而不是正确的符号 结果。”

这是什么意思?符号位是用结果的“值”设置的......这意味着它被设置为好像结果是无符号的?如果是这样,缺少第 33 位是怎么回事?

“由于我们只需要一个额外的位,因此只有符号位可能是错误的。”

这就是他们完全失去我的地方。

我从中得到的是,在添加有符号数字时,当且仅当符号位错误时才会出现溢出。因此,如果您添加两个正数并得到负数,或者如果您添加两个负数并得到正数。但我不明白他们的解释。

另外,这仅适用于无符号数,对吗?如果您要添加有符号数字,那么检测溢出肯定要简单得多。如果 ALU 的最后一个半加器设置了它的进位位,就会发生溢出。

注意:我真的不知道这里适合什么标签,请随意编辑。

【问题讨论】:

    标签: math binary hardware


    【解决方案1】:

    任何时候你想处理这些类型的 ALU 项目,无论是加法、减法、乘法等等,从 2 或 3 位数字开始,比 32 或 64 位数字更容易处理。在 2 位或 3 位之后,无论是 22 位还是 2200 位都无关紧要,从那时起,它的工作原理完全相同。基本上,如果您想制作一个包含所有 3 位操作数及其结果的表,以便您可以直观地检查整个表,但是您可以手动创建一个包含所有 32 位操作数和所有 32 位操作数及其结果的表,不能这样做在合理的时间内手工完成,无法目视检查整个表格。

    现在二元补码,这只是一个表示正数和负数的方案,它不是有原因的任意东西,疯狂的原因是你的加法器逻辑(也是减法器使用的东西,与乘法器使用的东西相同)不关心未签名或签名。它不知道区别。在我的三位世界中,程序员关心的是位模式 0b111 可能是正七(+7),也可能是负七。相同的位模式,将其输入到加法逻辑中,同样的东西出来了,出来的答案我可以选择解释为 unsignedtwos 补码(这么久因为我将操作数和结果全部解释为 unsigned 或全部作为 twos 补码)。二进制补码还具有对负数设置最高有效位 (msbit) 的特性,对于正数则为零。所以它不是符号加幅度,但我们仍然谈论 msbit 是符号位,因为除了它告诉我们的两个特殊数字,数字的符号,其他位实际上告诉我们它们的幅度只是不像 符号+幅度符号中可能有的无符号幅度。

    所以,整个问题的关键是了解你的极限。对于 3 位无符号数,我们的范围是 0 到 7、0b000 到 0b111。对于 3 位有符号(二进制补码)解释,我们的范围是 -4 到 +3(0b100 到 0b011)。现在,如果您添加 7+1,0b111 + 0b001 = 0b1000,现在将我们限制为 3 位,但我们只有一个 3 位系统,因此 0b000,7+1 = 8,我们不能在我们的系统中表示 8,所以这是溢出,因为我们碰巧将这些位解释为无符号位,我们查看“无符号溢出”,也称为进位位或标志。现在,如果我们采用相同的位但将它们解释为有符号,则 0b111 (-1) + 0b001 (+1) = 0b000 (0)。减一加一是零。没有溢出,没有设置“有符号溢出”...什么是有符号溢出

    首先什么是“无符号溢出”。

    无论我们的寄存器中有多少位,“一切都一样”的原因与以 10(十进制)为基数的小学数学没有什么不同。如果你添加 9 + 1,它们都在个列中,你说 9 + 1 = 零携带 1。你将 1 带到十列,然后 1 加 0 加 0(你在十列中填写了两个零)是 1 携带零。您在十列中有一个 1,在个列中有一个零:

      1
      09
     +01
    ====
      10
    

    如果我们声明我们仅限于个列中的数字,那么十列就没有空间了。那么进位位非零意味着我们有溢出,为了正确计算我们需要另一列的结果,与二进制相同:

      111 
       111
     + 001
    =======
      1000
    

    7 + 1 = 8,但是如果我们声明一个 3 位系统,我们不能做 8,我们可以在设置进位位的情况下做 7 + 1 = 0。这就是 双补 的美妙之处:

      111 
       111
     + 001
    =======
       000
    

    如果您查看上述三位加法,则无法通过查看是否设置进位位的 7 + 1 = 0 或 -1 + 1 = 0 来判断。

    所以对于无符号加法,正如我们从小学开始就知道的那样,将非零的东西结转到下一列意味着我们已经溢出了很多占位符,需要一个占位符,再一个列,保存实际答案。

    有符号溢出。学术上的答案是如果 msbit 列的 carry incarry out 不匹配。让我们在我们的 3 位世界中举一些例子。因此,对于 twos 补码,我们被限制在 -4 到 +3 之间。因此,如果我们添加 -2 + -3 = -5 将无法正常工作? 为了弄清楚减二是多少,我们进行反转并添加一个 0b010,反转 0b101,添加一个 0b110。减三是 0b011 -> 0b100 -> 0b101

    所以现在我们可以这样做了:

      abc
      100 
       110
     + 100
    ======
       010
    

    如果你看b下的数字是“carry in”到msbit列,a下的数字是1,是carry out , 这两个不匹配所以我们知道有一个“signed overflow”。

    让我们试试 2 + 2 = 4:

      abc
      010
       010
     + 010
    ======
       100
    

    你可能会说,但这看起来是对的,当然 unsigned 确实如此,但是我们在这里进行 signed 数学运算,所以结果实际上是 -4 而不是正数 4 . 2 + 2 != -4。 b下的carry in是1,msbit的carry out是0,carry in执行不匹配。 签名溢出

    无需查看carry in(或carry out)即可找出已签名的溢出if ( msbit(opa) == msbit(opb) ) && ( msbit(res) != msbit(opb) ) 有符号溢出,否则没有有符号溢出。 opa 是一个操作数, opb 是另一个操作数, res 是结果。

       010
     + 010
    ======
       100
    

    取这个 +2 + +2 = -4。 msbit(opa) 和 msbit(opb) 相等,结果 msbit 不等于 opb msbit,所以这是 有符号溢出。您可以使用此表来考虑它:

    x ab cr 
    0 00 00
    0 01 01
    0 10 01
    0 11 10 signed overflow
    1 00 01 signed overflow
    1 01 10
    1 10 10
    1 11 11
    

    这个表是所有可能的组合,如果carry in bit,operand a,operand b,carry out and result bit for a single column 将头侧向左排序看到这个 x 是 carry in,a 和 b 列是两个操作数。 cr 作为一对是 011 的结果 xab 表示 0+1+1 = 2 十进制,即 0b10 二进制。因此,按照我们规定的规则,如果 carry incarry out 不匹配,那就是 signed overflow。那么 x 列中的项目与 c 列中的项目不匹配的两种情况表示为 a 和 b 输入相互匹配但结果位与 a 和 b 相反的情况。所以假设规则是正确的,这个不需要知道进位位是什么的快速快捷方式会告诉你是否有有符号溢出

    现在您正在阅读一本 H&P 书籍。这可能意味着 mips 或 dlx,mips 或 dlx 都不像大多数其他处理器那样处理进位和 签名标志。 mips 不是最好的第一个指令集 IMO 主要是因为这个原因,他们的方法在任何方面都没有错,但是作为一个古怪的人,你将永远花在不同的思考上,并且在使用大多数其他处理器时不得不进行翻译。如果您学习了典型的 znvc 标志(零标志、负标志、v=有符号溢出、c=carry 或 无符号溢出)方式,那么您只需在进行时进行翻译到mips。通常这些是在每个 alu 操作上计算的(对于非 mips 类型的处理器),您将看到有符号和 无符号溢出 正在为加法和减法计算。 (我习惯了较旧的 mips,也许这一代的书和当前的指令集有一些不同)。在了解了上述关于加法器电路如何不关心有符号与无符号的所有内容之后,在 mips 的开头将其称为 addu add unsigned 是 mips 的一个大问题,它确实让您陷入错误的心态来理解这么简单的事情。导致人们相信有符号加法和无符号加法之间存在差异。只有溢出标志的计算方式不同。现在乘法和除法肯定有一个二进制补码 vs 无符号差,理想情况下你需要一个有符号乘法和一个无符号乘法 或者你需要处理这个限制。

    我推荐一个简单的(取决于你的位操作的强度和二元补码)你可以用一些高级语言编写的练习。基本上取所有无符号数 0 到 7 的组合加到 0 到 7 并保存结果。以十进制和二进制打印(操作数三位,结果四位),如果结果大于 7,则打印溢出。使用带符号的变量重复此操作,使用数字 -4 到 +3 添加到 -4 到 +3。打印带有 +/- 符号的十进制和二进制。如果结果小于 -4 或大于 +3 则打印溢出。从这两个表中,您应该能够看到上面的规则是正确的。严格查看允许大小的操作数和结果位模式(在这种情况下为三位),您将看到加法运算给出相同的结果,对于给定的一对输入,相同的位模式与这些位模式是否被认为是无符号的无关或二进制补码。您还可以验证 无符号溢出 是当结果需要使用第四列时,msbit 有一个 执行。当 carry incarry out 不匹配时进行签名,您可以使用查看操作数和结果的 msbits 的快捷方式看到。更好的是让你的程序进行这些比较并打印出一些东西。因此,如果您在表中看到结果大于 7 的注释,并且在表中看到结果中设置了位 3 的注释,那么您将看到无符号表始终如此(仅限于输入 0至 7)。而更复杂的一种,有符号溢出,总是在结果小于-4大于3的时候,操作数高位匹配,结果高位不匹配操作数的时候。 p>

    我知道这非常长而且非常初级。如果我在这里完全错过了标记,请发表评论,我将删除或重写此答案。

    双补魔法的另一半。硬件没有减法逻辑。 “转换”为二进制补码的一种方法是“反转和加一”。如果我想使用 twos 补码 减去 3 - 2,实际发生的情况与 +3 + (-2) 相同,并且从 +2 到 -2 我们反转并添加一。看看我们小学的加法,你有没有注意到第一列 carry in 的洞?

      111H
       111
     + 001
    =======
      1000
    

    我在洞所在的位置上方放了一个 H。那么 carry in 位被添加到操作数中,对吧?我们的加法逻辑不是一个二输入加法器它是一个三输入加法器是吗?大多数列必须添加三个一位数才能计算两个操作数。如果我们在第一列上使用三输入加法器,现在我们有一个地方可以......加一个。如果我想减去 3 - 2 = 3 + (-2) = 3 + (~2) + 1 即:

        1
      011
    + 101
    =====
    

    在我们开始之前填写的是:

     1111
      011
    + 101
    =====
      001
    

    3 - 2 = 1。

    逻辑的作用是:

    if add then carry in = 0; b 操作数不反转,执行 不反转。 如果减去则进位 = 1; b 操作数被反转,执行可能被反转。

    上面的加法显示了一个执行,我没有提到这是一个无符号运算3 - 2 = 1。我使用了一些二元补码的技巧来执行一个无符号操作,因为无论我将操作数解释为有符号还是无符号,相同的规则都适用于加法或减法。为什么我说 carry out 可能会被反转,因为有些处理器会反转 carry out 而有些则不会。它与级联操作有关,例如 32 位加法逻辑并使用进位标志和带进位的加法或带借位的加法或减法指令创建 64 位加法或减法,或任何倍数基址寄存器大小。假设您在 32 位系统中有两个 64 位数字 a:b + c:d 其中 a:b 是 64 位数字,但它保存在两个寄存器 a 和 b 中,其中 a 是上半部分,b 是下半部分一半。所以 a:b + c:d = e:f 在一个 32 位无符号系统上,它有一个进位并用进位相加:

    add f,b,d
    addc e,a,c
    

    add 将其 carry out 位从状态寄存器的进位标志的最高位通道中留下,addc 指令是 add with carry 采用操作数 a+c,如果进位位被设置,然后再增加一个。 a+c+1 将结果放在 e 中,执行在进位标志中,所以:

    add f,b,d
    addc e,a,c
    addc x,y,z
    

    是 96 位加法,以此类推。这里又是一些对 mips 非常陌生的东西,因为它不像其他处理器那样使用标志。对于签名执行,反转或不反转的地方是特定处理器的借位减法。减法:

    如果减去则进位 = 1; b 操作数被反转,执行可能被反转。

    对于借位减法,如果状态寄存器中的进位标志指示借位,则 carry in 为 0,否则 carry in 为 1 ,并且您必须将 执行 放入状态寄存器以指示借用。

    基本上对于正常的减法,一些处理器将 b 操作数取反并在输入的过程中进行 在输出的过程中执行,一些处理器将 b 操作数取反并在输出过程中进行 > 进入但不要在退出时倒置执行。然后当你想做一个条件分支时,你需要知道进位标志是大于还是小于(通常语法会有一个分支如果大于或小于分支,有时会告诉你如果进位哪个是简化分支如果进行清除,则设置或分支)。 (如果你没有“明白”我刚才所说的话,那是另一个同样长的答案,只要你正在研究 mips,它就没有任何意义)。

    【讨论】:

    • 嗯,你肯定已经和任何人一样解释了它。非常感谢您抽出宝贵时间。但是,我仍然觉得我不明白为什么这个技巧有效。如果进位和进位不同,为什么会发生溢出的背后是否没有简单的数学直觉?
    • 当然,符号位发生变化,将两个正数相加得到一个负数,或者将两个负数相加得到一个正数,你的位用完了,需要更多。就像无符号溢出到下一列一样。
    【解决方案2】:

    作为 32 位有符号整数,由 1 个符号位和 31 位表示实际数字,我们实际上是将两个 31 位数字相加。因此,第 32 位(符号位)将是溢出可见的位置。

    “缺少第 33 位意味着发生溢出时,符号位被设置为结果的值,而不是结果的正确符号。”

    想象以下两个正数相加(简化为 16 位):

      0100 1100 0011 1010  (19514)
    + 0110 0010 0001 0010  (25106)
    = 1010 1110 0110 1100  (-20884 [or 44652]) 
    

    对于两个大负数的求和,但是需要额外的位

      1100 1100 0011 1010  
    + 1110 0010 0001 0010  
    =11010 1110 0110 1100  
    

    通常,CPU 将这个第 33 位(或它在 +1 上运行的任何位大小)暴露为微架构中的溢出位。

    【讨论】:

    • 不完全。第 33 位是第 32 位的进位(从 1 开始编号)。溢出位计算为该进位与进入位 32(即位 31 的进位)的异或。
    【解决方案3】:

    它们的描述与对具有特定位序列的值的操作有关:第一位对应于值的符号,其他位与该值的大小有关。

    这是什么意思?符号位用结果的“值”设置...

    他们的意思是 overflow 位 - 这是添加需要溢出到下一个数字的两个数字的结果 - 被转储到符号位应该在的相同位置。

    “由于我们只需要一个额外的位,因此只有符号位可能是错误的。”

    这意味着,当您执行溢出的算术时,值可能不正确的唯一位是符号位。所有其他位仍然是它们应该是的值。

    这是上面描述的结果:由于溢出导致符号位的值混淆。

    【讨论】:

    • 顺便提一下,这是 2s 补码。您说“第一位对应于值的符号,其他位是该值的大小” - 但在 2s 补码中情况并非如此,对吧?
    • @JackM 你说得对,我没有意识到这是 2 的补码。但是,就这个问题而言,确切的表示是不相关的 - 除了符号和值位的顺序。
    • 我真的很困惑为什么你(和我的教科书,想想看)提到一个“符号位”......这不是符号和mag,它是两个补充。 2 的补码基于以下观察结果:当以算术模 2^n 工作时,2^n - x 等效于 -x。所以没有明确的符号位......也许我应该作为一个单独的问题问这个问题,因为它让我困惑了一段时间。
    • @JackM 在 2 的补码表示中,高位仍然充当符号位。对于负数,它被设置,而对于正数,它不是。
    猜你喜欢
    • 2015-12-24
    • 2021-10-05
    • 1970-01-01
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 2023-02-13
    相关资源
    最近更新 更多