“4 - 12”和“4 + (-12)”有什么区别?

What is the difference between “4 - 12” and “4 + (-12)”?

我尝试比较下一个表达式:

1) 
    mov al, 4
    mov bl, 12
    sub al, bl ; CF == 1

    0000 0100 (4)
   +
    1111 0100 (-12)
   = 
    1111 1000 (-8 == 248) 

2) 
    mov al, 4
    mov bl, -12
    add al, bl ; CF == 0

    0000 0100 (4)
   +
    1111 0100 (-12)
   = 
    1111 1000 (-8 == 248) 

结果相同,但进位标志不同。为什么?通过二进制补码加法实现的减法

x86 上的减法不等于二进制补码的加法。要了解进位标志将假定的值,您必须改为执行长减法:

    0000 0100
-   0000 1100
-------------
  1 1111 1000

看看最后怎么还剩下借位?这个借位是设置进位标志的东西(即进位等于借位)。

像 ARM 这样的一些其他架构确实将减法作为加法来实现,但它们不是作为二进制补码的加法,而是作为一个补码的加法和一个额外的进位。这在减去 0 时很重要.

例如,您的方法将产生 12 − 0:

    0000 1100
+   0000 0000 (- 0000 0000 => + 0000 0000)
-------------
  0 0000 1100

有明确的进位。但实际发生的是

    0000 1100
+   1111 1111 (- 0000 0000 => +1111 1111 + 1)
+           1
-------------
  1 0000 1100

带进位。这个细节很重要,否则与 0 的比较将无法正常工作。在这个方案中,只要没有借位就表示进位(即进位补借位)。

Intel 的做法和 ARM 的做法实际上总是产生相同的结果,只是进位标志正好相反。因此,只要 ARM 设置进位,Intel 就会清除它,反之亦然。

这两种减法语义的方法都相当普遍。 ARM 方法稍微更容易实现,因为它允许直接使用加法器进行减法,而无需接触进位。使用 Intel 方法,在执行减法时必须对进位进位和进位出位进行补充,但这样做的额外门在宏伟的计划中并不重要。另一方面,英特尔的方法对程序员来说更直观,因为如果您将正在执行的操作可视化为长减法,那么将进位标志也视为借位更有意义。