如何检测二进制补码中的溢出?

16

我发现使用 二进制补码 进行正负数相减时,会产生溢出。例如,当我从2中减去1时,结果为:

2 = 0010
1 = 0001 -> -1 = 1111
2 + (-1) -> 0010 + 1111 = 10001

这里的结果有第五位为10001 - 是否溢出?我找到了下列用于检测二进制补码溢出的规则:

如果两个正数相加的结果为负数,则发生了溢出。如果两个负数相加的结果为正数,则发生了溢出。否则,未发生溢出。

请问是否有人可以进一步解释这些规则并给出示例?


你最后的例子是不一致的。-1的值是用四位给出的,但你的答案是用五位计算的。如果你的字长是5位,则-1的值应该是11111,而不是1111。在5位字长中,1111是值15,而不是-1。你计算出2 + 15 = -15。另外,你的编程问题是什么?(这还不是一个真正的编程问题。) - Raymond Chen
1
抱歉,我不明白。我将“-1”转换为四位二进制数,然后相加得到了五位。我应该如何改变操作? - Max Koretskyi
1
@RaymondChen,好的,请问您能否向我展示如何使用四位二进制数进行2-1运算? - Max Koretskyi
1
@RaymondChen,我正在尝试理解如何减去两个数字,例如 2 - 1 - Max Koretskyi
1
@M.M,请问您能演示一下如何计算2-1吗? - Max Koretskyi
显示剩余5条评论
2个回答

41

让我们从回答您标题问题开始。

如何检测二进制补码中的溢出?

溢出规则:如果两个具有相同符号(都为正数或都为负数)的数字相加,则当且仅当结果具有相反符号时会发生溢出。

但是,在您的示例之后,您在问题正文中询问了其他内容。

因此,这里的结果是第五个左比特10001——是否溢出?

不!这里没有溢出。第五个比特是进位/借位。如果您正在进行加法,则进位;如果您正在进行减法,则是借位。

当您试图表示的数字超出可以表示的数字范围时,将发生溢出。在您的示例中,您使用了4位二进制补码,这意味着您可以表示范围内的任何数字-81000)到+70111)。您的减法2-1的结果是+1,属于表示范围内的数字。

当我们添加一个负数和一个正数操作数时,结果将始终在表示范围内。当我们添加两个具有相同符号(都为正数或都为负数)的数字并且结果具有相反符号时,会发生溢出。

大多数关于进位和溢出的误解源于我们使用进位作为生成溢出标志的参数之一。它们密切相关但不是同一件事。

在二进制补码中添加数字时,如果进位和最高位(符号位)的进位不同,则意味着发生了溢出。

让我们看一个产生正结果的两个负操作数:

-8 + (-1) = -9 

 1000  (carry)
  1000 (-8)
+ 1111 (-1)
------
  0111 (+7) OVERFLOW!

进位为1,且最高有效位(MSB)进位为0。

现在,举一个两个正操作数得到负结果的例子。

+7 + 1 = +8

 0111  (carry)
  0111 (+7)
+ 0001 (+1)
------
  1000 (-8) OVERFLOW!

进位为0,且符号位(最高位)进位为1。


谢谢,我会稍微了解一下进位标志溢出标志,然后再回来问问题。 - Max Koretskyi
我将澄清这个问题。在-9的情况下,实际答案是10111。而在+8的情况下,答案应该是01000。那么计算机如何知道溢出位是0还是1? - Midhunraj R Pillai
许多处理器将“符号”位标志定义为“负异或2s补码溢出”。你已经证明了溢出实际上是“负异或进位”。但这是否意味着“符号”位始终与进位相同?在2s补码加法中,有没有反例表明“负异或溢出”与“进位”不同?但如果它们总是相同的,那么为什么要有符号标志呢? - sprinter
对于2的补码,符号位只是最左边的比特位。2的补码的美妙之处在于,符号位的计算与任何其他比特位一样简单。“您已经说明了溢出实际上是'负数异或进位'”,不!结果的符号位异或进位。“但是这是否意味着'sign'位始终与carry相同?” 不是,并且正是这种差异使我们用它来识别溢出。“有反例吗...” 检查我给出的示例,但这次请使用结果的符号位和进位标志。更多示例请参见https://en.wikipedia.org/wiki/Two%27s_complement。 - GabrielOshiro
1
@GabrielOshiro,感谢您的解释。我意识到我的评论非常模糊 - 抱歉。当我发表那篇文章时,我实际上是在想ATMEGA328 MCU,它在其状态寄存器中具有单独的进位(C),符号(S),2的补码溢出(V)和负数(N)标志。 N标志是2的补码符号(即MSB)。 S标志被定义为“总是V异或N”。您清晰地解释了如何计算V(作为C异或N),这让我想知道该处理器上S标志的意义。在数据手册中,它并没有被很好地解释。 - sprinter
显示剩余3条评论

4

@GabrielOshiro的回答非常好。我只想在这里添加一点逻辑。当你在这里将2和-1相加时,

2 = 0010
1 = 0001 -> -1 = 1111
2 + (-1) -> 0010 + 1111 = 10001

在负数中,您应该将最高位与其他位分开,因为在二进制补码中,该位带来的是负数值。因此,如果您先将其他所有位相加:

0010 + 0111(leave out the leftmost 1 for now) = 1001

在此之后,我们可以清楚地看到,“10001”中的第五位是由于将我们先前留下的“1”(在第四位)与1001相加导致第五位进位造成的。然而,由于这个“1”实际上应该与1001相抵消,让我们只剩下0001,因此我们可以安全地忽略“10001”中的额外一位。
更深入的推理是考虑何时我们可以安全地忽略这个额外的位,何时不能。正如@GabrielOshiro所提到的那样,当最高有效位的进位和借位不同时,我们就不能忽略它。在负数的情况下,从进位中丢失了两个单位,因为没有空间来容纳额外的位,而在正数的情况下,从借位中丢失了两个单位,因为本应是正数单位的东西被认为是负数单位。这里1-(-1)= 2。因此,进位和借位会互相抵消。但是,当只发生其中一个时,结果将是不正确的,因此我们会溢出。

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