使用二进制补码进行逐位减法溢出

vai*_*dil 5 c bit-manipulation bitwise-operators

当使用二进制补码执行逐位减法时,如何知道何时应忽略溢出?我读过的几个网站都说过溢出只是被忽略了,但这并不总是有效 - 溢出对于像这样的问题是必要的-35 - 37,因为需要额外的数字来表达答案-72.

编辑:这是一个例子,使用上面的等式.

35到二进制 - > 100011,找到两个补码,使其为负数:011101

37到二进制 - > 100101,找到两个补码,使其为负数:011011

执行上述术语的添加(二进制等效-35 - 37):

011101
011011
------
111000
Run Code Online (Sandbox Code Playgroud)

取两个补码转换回正数: 001000

以上是许多网站(包括学术网站)所说的答案应该是,因为你忽略溢出.然而,这显然是不正确的.

amd*_*mdn 5

当结果无法在目标数据类型中表示时,会发生溢出.值-72可以用char表示,这是一个带符号的8位数量......在你的例子中没有溢出.也许你正在考虑一个borrow一边做减法按位...当你减去'1''0'你需要borrow从下一个高阶位的位置.在进行减法时,你不能忽略借用.

-35 decimal is   11011101 in two's complement 8-bit
+37 decimal is   00100101 in two's complement 8-bit
Run Code Online (Sandbox Code Playgroud)

从最低有效位到最高有效位从右到左,你可以在-35中从每个位中减去+37中的每一位,直到你到达第5位(计数从右边的第0位开始).在位5处需要减去'1','0'因此需要在-35中借位6位(下一个更高位),这恰好是'1'借位之前的位.结果看起来像这样

-35 decimal is   11011101 in two's complement 8-bit
+37 decimal is   00100101 in two's complement 8-bit
                 --------
-72 decimal is   10111000 in two's complement 8-bit
Run Code Online (Sandbox Code Playgroud)

结果为负,并且您在8位二进制补码中的结果具有高位设置(位7)...这是负数,因此没有溢出.

更新:我想我看到了混乱的地方,我声称这里的答案加上和减去两个补码是错误的,当它说你可以discard the carry (indicates overflow).在那个答案中,他们通过使用二进制补码然后加法将第二个操作数转换为负数来进行减法.那很好 - 但是在这种情况下,进位并不代表溢出.如果你在N位(编号为0到N-1)中添加两个正数,并且你考虑这个无符号算术range 0 to (2^N)-1并且你得到位位置N-1的进位,那么你就有溢出 - 两个正数之和(解释为无符号)为了最大化可表示的正数的范围,不应该产生最高位(位N-1)的进位.因此,当添加两个正数时,您可以通过说明来识别溢出

  1. 当你将它们解释为无符号时,必须没有执行N-1位
  2. 当解释为有符号时,位N-1的结果必须为零(二进制补码)

但请注意,处理器不区分有符号和无符号加法/减法......它们设置溢出标志以指示如果您将数据解释为已签名,无法表示结果(错误).

这里是进位和溢出标志的非常详细的解释.这篇文章的内容就是这样

  • unsigned算术中,观察进位标志以检测错误.
  • unsigned算术中,溢出标志告诉你没有什么有趣的.

  • signed算术中,观察溢出标志以检测错误.

  • signed算术中,进位标志告诉你没什么有趣的.

这是一致的维基百科算术溢出的定义,它说

大多数计算机区分两种溢出条件.当加法或减法的结果(考虑操作数和结果为无符号数)不适合结果时,会发生进位.因此,在添加或减去被解释为无符号值的数字后检查进位标志很有用.当结果没有可以从操作数的符号预测的符号时(例如,在添加两个正数时为负结果),就会发生溢出.因此,在添加或减去以二进制补码形式表示的数字(即它们被视为带符号数字)之后检查溢出标志是有用的.

  • 好吧,这会改变事情......如果你仅限于 6 位数字,那么你在表示 -35 和 +37 时就会遇到问题,这两个数字都需要 7 位二进制补码来表示。在二进制补码中,6 位数字的范围是 -32 到 +31(二进制 100000 到二进制 011111)。 (2认同)