二进制级别上如何检测溢出?

12
我正在阅读 Hennessey和Patterson(第4版)的教材《计算机组成与设计》。在第225页,他们描述了如何在带符号的二进制补码算术中检测溢出。但我甚至无法理解他们在说什么。
“当[溢出]确实发生时,我们如何检测它?显然,两个32位数字相加或相减可能产生一个结果,需要33位才能完全表示。”
好的。它不需要34位,因为即使是最小的34位数字也是最小的33位数字的两倍,而我们只是在加32位数字。
“缺少第33位意味着当发生溢出时,符号位将被设置为结果的值,而不是结果的正确符号。”
这是什么意思?符号位以结果的“值”设置...是否意味着它被设置为无符号的结果?如果是这样,那么这与缺少第33位的原因有什么关系?
“由于我们只需要一个额外的位,所以只有符号位可能是错误的。”
这就是他们完全失去我理解的地方。
我从中得到的是,在添加带符号的数字时,只有当符号位是错误的时才会发生溢出。因此,如果您添加两个正数并得到一个负数,或者添加两个负数并得到一个正数,则会发生溢出。但是我不理解他们的解释。
此外,这仅适用于无符号数字,对吗?如果您正在添加带符号的数字,那么检测溢出肯定要简单得多。如果ALU的最后一个半加器设置了进位位,就会发生溢出。
注意:我真的不知道在这里使用哪些标记是合适的,请随意编辑它们。
3个回答

19
任何时候,当你要处理这些ALU项目(例如加、减、乘等)时,请从2或3位数字开始,比32或64位数字更容易掌握。在2或3位之后,无论是22位还是2200位,它们的工作方式都完全相同。基本上,如果你想要手动制作一个包含所有3位操作数及其结果的表格,以便可以通过视觉检查整个表格,但是对于所有32位操作数与其结果的表格,无法在合理的时间内手动完成,并且无法通过视觉检查整个表格。
现在,二进制补码只是一种表示正负数的方案,而不是任意的东西,它有一个原因,这种疯狂的原因是你的加法逻辑(减法器使用的也是同样类型的乘法器)不关心无符号或有符号。它不知道区别。在我的三位世界中,比特模式0b111可以是正七(+7),也可以是负一,同一比特模式,将其输入到加法逻辑中,得到的结果相同,我可以选择将结果解释为无符号或二进制补码(只要我将操作数和结果全部解释为无符号或全部解释为二进制补码)。二进制补码还具有这样的特征,即对于负数,最高有效位(msbit)被设置为1,对于正数,它为0。因此,它不是符号加大小,但我们仍然谈论msbit作为符号位,因为除了两个特殊的数字外,它告诉我们的是数字的符号,其他位实际上告诉我们的是大小,只不过它们不是你可能在符号+大小表示法中使用的无符号大小。
因此,这个问题的关键在于理解你的限制。对于一个3位无符号数字,我们的范围是0到7,0b000到0b111。对于一个3位有符号(二进制补码)解释,我们的范围是-4到+3(0b100到0b011)。现在限制自己只使用3位,如果你把7加1,0b111 + 0b001 = 0b1000,但我们只有一个3位系统,所以它是0b000,7+1=8,在我们的系统中不能表示8,所以这是一种溢出,因为我们刚好把位解释为无符号的,我们看到了"无符号溢出",也称为进位标志或标志位。现在如果我们以相同的位数但解释为有符号的,那么0b111 (-1) + 0b001 (+1) = 0b000 (0)。减一加一等于零。没有溢出,"有符号溢出"没有设置...什么是有符号溢出
首先,什么是"无符号溢出"。
之所以"无论我们的寄存器有多少位,它都是一样的",与小学数学的十进制数字没有任何区别。如果你把两个都在个位上的9和1相加,你会说9+1=0进位1。你把一个1带到十位上,然后是1加0加0(你在十位上填了两个零),是1进位0。你在十位上有一个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列的进位进位不匹配。让我们在我们的三位世界中举些例子。因此,使用二进制补码,我们被限制在-4至+3之间。所以如果我们加上-2 + -3 = -5,那行不通,对吧? 要找出减去两个的结果,我们进行反转并添加一个操作:0b010,反转后为0b101,再加1得到0b110。减去三个是0b011-> 0b100-> 0b101

现在我们可以这样做:

  abc
  100 
   110
 + 101
======
   011

如果您看一下 b 下面的数字,那就是 msbit 列的“进位”,而 a 下面的数字 1,则是“溢出”,由于这两个数字不匹配,所以我们知道存在“有符号溢出”。

让我们试试 2 + 2 = 4:

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

你可能会说,看起来这没问题,因为使用了无符号数,但我们要进行有符号数的运算,所以结果实际上是-4而不是正4。2+2!= -4. 在b下面的“进位”是1,在最高有效位的“进位”是0,“进位”和“溢出位”不匹配。有符号溢出。
判断是否发生了有符号溢出有一种快捷方法,不需要查看“进位”(或“进位”):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

这个表格展示了单列的所有可能组合,包括进位位、操作数a、操作数b、进位位和结果位。将头向左转动可以看到x代表进位位,a和b代表两个操作数。成对的"cr"是结果"xab",011表示0+1+1=2十进制,即0b10二进制。因此,根据规则,如果进位位和进位后的位不匹配,就会发生有符号溢出。那么,当x列中的项目与c列中的项目不匹配时,就会出现这种情况,这些情况是a和b输入相互匹配,但结果位与a和b相反。因此,假设规则是正确的,这个快速的方法不需要知道进位位,就可以告诉你是否存在有符号溢出。
现在你正在阅读一本H&P书。这可能意味着MIPS或DLX,MIPS或DLX在处理进位和有符号标志方面与大多数其他处理器不同。在我看来,MIPS不是最好的第一个指令集,主要原因是它们的方法并没有错,但是作为一个另类,你会花费很长时间去思考不同之处,并且在转移到大多数其他处理器时需要进行翻译。如果您学习了典型的znvc标志(零标志、负标志、v=有符号溢出、c=进位或无符号溢出),那么只需要在转移到MIPS时进行翻译。通常,这些标志在每个ALU操作(对于非MIPS类型的处理器)上计算,您将看到加法和减法的有符号和无符号溢出被计算。(我习惯于旧版的MIPS,也许这一代书籍和当前指令集有所不同)。在学习了有关加法器电路不关心有符号还是无符号的所有内容之后,立即称之为"addu add unsigned"是MIPS的一个巨大问题,它真正使你误解了这个简单的概念。导致人们相信有符号加法和无符号加法之间存在差异,而实际上只是溢出标志被计算方式不同。现在,对于乘法和除法,肯定存在二进制补码无符号差异,理想情况下,您需要一个有符号乘法和一个无符号乘法,或者您需要解决这种限制。
我建议一个简单的练习(取决于你的位运算和二进制补码的强度),你可以用一些高级语言来编写。基本上,将无符号数字0到7的所有组合相加并保存结果。将它们以十进制和二进制(三位操作数,四位结果)打印出来,如果结果大于7,则打印溢出。使用数字-4至+3添加到-4至+3使用有符号变量重复此过程。打印带有+/-符号的十进制数和二进制数。如果结果小于-4或大于+3,则打印溢出。从这两个表格中,您应该能够看到上述规则是正确的。严格查看所允许的大小(在此情况下为三位),您将看到加法操作为给定输入对的结果提供了相同的结果,相同的位模式,不考虑这些位模式是否被视为无符号或二进制补码。另外,您可以验证当结果需要使用第四列时,无符号溢出是指,msbit有一个进位。对于有符号数,当进位进位不匹配时,您可以使用快捷方式查看操作数和结果的msbit来看到。更好的方法是让您的程序进行这些比较并打印出某些内容。因此,如果您在表格中看到一个注释,说明结果大于7,并且在表格中看到一个注释,说明结果中设置了位3,则对于无符号表格,这总是正确的(限于输入为0至7)。而更复杂的有符号溢出则始终在结果小于-4且大于3以及操作数上位匹配且结果上位与操作数不匹配时发生。

我知道这非常长而且非常基础。如果我完全错过了要点,请评论并我会删除或重新编写此答案。

二进制补码的另一半神奇之处在于硬件没有减法逻辑。将其"反转并加一"是将其转换为二进制补码的一种方法。如果我想用二进制补码减去3-2,实际上相当于+3 + (-2),然后从2到-2我们反转并加一.看看我们的小学加法,你注意到第一列的进位有个空洞了吗?

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

我在洞口上方标了一个 H。那么,那个“carry in”位是加到操作数中的,对吗?我们的加法逻辑不是两个输入的加法器,而是三个输入的加法器,是吗?大多数列必须添加三个一位数字才能计算出两个操作数。如果我们在第一列使用一个三输入加法器,现在就有一个地方可以…添加一个。如果我想要执行减法 3-2 = 3 + (-2) = 3 + (~2) + 1,即:

    1
  011
+ 101
=====

在我们开始填写之前,需要注意以下几点:

 1111
  011
+ 101
=====
  001

3 - 2 = 1.

这个逻辑的作用是:

如果是加法,则进位=0;b操作数不被反转,进位不被反转。如果是减法,则进位=1;b操作数被反转,进位可能被反转。

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

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

在状态寄存器的进位标志中,加法指令从最高位的进位位置处留下其carry out位,而addc指令是带进位加法,它将操作数a+c相加,如果进位位被设置,则再加1。即a+c+1,并将结果放入e寄存器,将carry out放入进位标志中,因此:

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

这是一个96位的加法,对于MIPS来说,这很陌生,因为它不像其他处理器一样使用标志。在有符号进位的情况下,借位或不借位取决于特定处理器的带借位减法。对于减法:

如果是减法,则进位=1;b操作数被反转,可能会反转进位

对于带借位减法,您必须说明状态寄存器中的进位标志是否表示借位,如果是,则进位为0,否则进位为1,并且您必须将进位输出到状态寄存器以指示借位。

基本上,对于普通的减法,某些处理器会在输入时反转b操作数和进位,然后在输出时反转进位和进位,而某些处理器会在输入时反转b操作数和进位,但在输出时不会反转进位。然后,当您想要进行条件分支时,您需要知道进位标志是否表示大于还是小于(通常语法将有一个“如果大于跳转”或“如果小于跳转”,有时会告诉您哪一个是当进位设置或进位清除时简化的分支)。 (如果您不理解我刚才说的话,那么这是另一个同样冗长的答案,只要您在学习MIPS,就不会有任何意义。)


嗯,你确实解释得很好。非常感谢你花时间解答。但是,我仍然觉得我不明白为什么那个技巧有效。难道在溢出发生的时候,进位和退位不相等没有一个简单的数学直觉吗? - Jack M
4
当两个正数相加得到负数或两个负数相加得到正数时,符号位会改变。这意味着当你用完了所有比特位并需要更多时,就像无符号数溢出到下一列一样。请注意,翻译过程中不改变原文的意思,并尽可能使语言通俗易懂。 - old_timer

1

由于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位的进位进行异或运算得出的(第32位是从第31位进位的)。 - Alexey Frunze

0

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

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

他们的意思是,溢出位 - 由于将需要溢出到下一个数字的两个数字相加而产生的位 - 被转储到应该是符号位的同一位置。

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

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

这是上面所述的混淆符号位值的后果。


顺便提一下,这是二进制补码。你说“第一位对应值的符号,其他位是该值的大小”——但在二进制补码中并非如此,对吧? - Jack M
@JackM 你说得对,我没有意识到这是二进制补码。然而,对于这个问题的目的来说,确切的表示并不重要 - 除了符号位和数值位的顺序之外。 - cheeken
我其实很困惑为什么你(以及我所使用的教材)会提到“符号位”……这不是补码吗?2的补码基于这样一个观察:在模2^n算术下,2^n - x等价于-x。因此,没有显式的符号位……也许我应该把这作为一个单独的问题来问,因为这已经困扰我一段时间了。 - Jack M
1
@JackM 在二进制补码表示法中,高位仍然充当符号位。对于负数它被设置为1,对于正数它被设置为0。 - bames53

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接