这是补码计算溢出错误吗?

Is this two's complement calculation overflow error?

假设有两个数字用8位二进制补码表示, 当我计算AB时,它是否溢出?

A = 54 = 00110110 ; B = -77 = 10110111

这是我的计算:

      00110110
-     10110111
--------------
   (1)11111111 = -1

小数计算:54 - (-77) = 131

从 131 开始!= -1

因此发生溢出

确实发生了正有符号整数溢出。但是,您的数学不正确。 -77的8位二进制补码值是0b10110*0*11,不是0b10110*1*11.

  00110110    (+54)
- 10110011  - (-77)
----------
  00110110    (+54)
+ 01001101  + (+77)
----------
  10000011    (u131 or -125)

因为很难想象二进制数的减法,所以通常更容易取反该数。但是,这里是相同的过程,但不这样做,一次只做一点:

  00110110
- 10110011
----------
  00110101
- 10110010
----------
  00110011
- 10110000
----------
  00100011
- 10100000
----------
  00000011
- 10000000
----------
  10000011   (borrow over MSb occurred)

这给出了相同的结果,但工作量更大,更容易导致错误。主要区别在于发生了从位向上的借位。

A = 54 = 00110110 ; B = -77 = 10110111

其实 -77 是 10110011

所以

a - -b = a + b

设置

        0
 00110110
+01001101
=========

并填写

011111000
 00110110
+01001101
=========
 10000011

msbit 的进位和出位不匹配。此外,操作数的 msbits 匹配,结果的 msbits 不匹配。两者都表示有符号溢出。 msbit 的进位为零意味着没有无符号溢出(如果这被视为加法,则不是)。

虽然是减法,但更正确

a - b = a + (-b) 和 -b = ~b + 1

所以你在逻辑上真正看到的是 b 操作数和进位倒置。

         1
  00110110
 +01001100
==========

这当然会给出相同的结果,但它是减法运算而不是加法运算。

你当然可以用二进制做减法,就像你在小学时用 10 进制做减法一样容易。

 00110110
-10110011
==========

与小学不同,我们会将较大的数字放在最前面,然后取反结果,但这里如果需要,我们会从末尾借用...

1       0
 00110110
-10110011
==========

1       2
 00110100
-10110011
==========
        1

再借

1      22
 00110000
-10110011
==========
  0000011

再借

02     22
 00110000
-10110011
==========
 10000011

这里的多余位是什么,是加法器的进位,不是借位。当借位没有发生时,进位为 1,当借位发生时,进位为 0。一些架构反转减法的进位并使其成为借位。有些不反转它,你必须知道不进位是借位。

二进制补码的美妙之处在于加法和减法没有无符号与有符号的概念。因此,从逻辑角度来看,位是位 54 - (-77) 也与 54 - 179 相同,后者肯定有借位。用户认为位模式是 -77 或 +179。除法和乘法可以关心,取决于您是否需要填充,并且您对有符号的填充进行符号扩展而不是对无符号的零扩展。如果操作数的大小正确,则可以使用有符号和无符号乘法来执行或运算。但是很大一部分操作数会溢出。适当的乘法(结果是操作数位数的 2 倍)对符号敏感。

54 - -77 = 131 大于 127,因此从带符号运算的角度来看,该结果不能放入 8 位中。所以这是一个带符号的溢出。

例如

54 + 205 是 259,它大于 256,因此这将是一个无符号溢出(进位位将为加法操作设置)。但这也是 -51, 54 - 51 = 3 不借用,所以这两种观点在这里也适用。 54 + 205 = 0x103 这意味着 0x03 进位是一个。

如果你解决了

操作数的msbits和msbit的进位全部相加 a 操作数 a 的 msbit,b 操作数 b 的 msbit,i msbit 的进位,c msbit 的进位,r msbit 的结果。 i != c 是一个带符号的溢出

ab i   cr
00 0 = 00
00 1 = 01 signed overflow
01 0 = 01
01 1 = 10
10 0 = 01
10 1 = 10
11 0 = 10 signed overflow
11 1 = 11

因此,如果 msbit 的进位和出位不匹配,则可以使用定义,这是有符号溢出。

00 1 = 01 signed overflow
   ^   ^
11 0 = 10 signed overflow
   ^   ^

或者你可以使用如果操作数的 msbits 匹配并且结果不匹配它是有符号溢出的定义

00 1 = 01 signed overflow
^^      ^
11 0 = 10 signed overflow
^^      ^


00 0 = 00 not a signed overflow
^^      ^ 
11 1 = 11 not a signed overflow
^^      ^

如果操作数的 msbits 不匹配,则不会有符号溢出。用程序或手工完成 3 位或 4 位操作数的整个组合,从带符号的角度检查它们,你会发现这是一个真实的陈述,你不必相信我。

有的人是用msbit的方式记住的(不用计算进位和出位就可以求值),有的人记得进位和进位是否匹配。因此,您会看到逻辑操作数以一种或另一种方式计算它,具体取决于作者。

一些体系结构文档 misleading/confusing 谈论进位位和溢出位,但在每次使用术语时都没有仔细研究,溢出实际上是带符号的溢出位。他们中的 None 甚至每次提到进位位时都将其称为无符号 overflow/signed borrow/carry 位......有些人甚至可能会使用溢出位对于乘法和加法,所以所有这些只会增加混乱。

最好的练习之一是编写一个小程序(只需要大约 20 - 30 行代码)为每个 16*16 操作提供 4 位操作数 0x0 到 0xF 的所有组合。因为这些标志在大多数情况下与比较一起使用,这是一个减法,然后进行减法(反转等并使用加法器)。并计算所有标志,CNVZ。有了那个 table 你可以明白为什么单独的 C 标志可以用作大于或小于,N==V,N!=V 等等。 Z 或 C set 与 C not set 相同,因此如果翻转操作数,则可以减少指令的数量,即大于等于小于或等于的倒数。反转操作数,您不需要使用 z 标志,您可以简单地使用 jc 或 jnc。但是这个练习也显示了 signed 大于 vs unsigned 等等。加上明显有符号和无符号溢出。