【问题标题】:Why is the Carry Flag set during a subtraction when zero is the minuend?当被减数为零时,为什么在减法期间设置进位标志?
【发布时间】:2016-11-05 02:45:30
【问题描述】:

目前,我为非常大的无符号整数编写了自己的小库,用于算术和逻辑运算。为了提高性能,我决定在汇编中实现一些功能。所以这是我的问题。 在减去两个无符号整数时,当我从 0 中减去任何数字时,就会设置进位标志。

但是为什么在这种情况下会设置进位标志呢?进位标志仅在发生溢出时设置,但如果我从零中减去任何数字,我不会得到溢出。 还是我错了?

【问题讨论】:

  • 注意:在汇编中编写自己的操作可能无法提供您预期的优化。编译器不检查内联汇编,也无法优化它们。如果您只提供逻辑 C 代码,您可能会发现编译器会在优化方面做得更好。
  • 感谢您的建议。但我不做内联汇编。我在汇编中编写单独的部分,并将此目标文件与 C 代码链接。
  • 那么在代码的整体结构中,汇编程序例程的优化机会就更小了。不要误会我的意思,我不是在尝试使用汇编程序来提高速度,我曾经认为它也总是会更快,但总是将代码与链接汇编的性能与直接编写的代码的性能进行比较具有完整编译器优化的 C(-O3 在大多数 -Ofast 上用于 gcc 版本 >= 4.6.0)。今天的编译器非常擅长优化,直接 C 的 9/10 倍会更快。
  • 好的,谢谢,我会记住的。我遇到的最大问题是,在 C 语言中没有简单的方法来检查进位标志的状态并在进一步的操作中使用它。所以目前我使用无符号整数类型的最高位作为进位标志。但我对此并不满意。这就是为什么我要在汇编程序中实现减法和加法的核心逻辑。但也许有人有更好的主意来做到这一点。我总是愿意接受其他建议。

标签: assembly x86 flags


【解决方案1】:

我们从小学就知道 a - b = a + (-b)。这就是我们不减去我们添加负数的逻辑方式。我们还从初学者编程课程中了解到,使用二进制补码来获得负数,您可以反转并添加一个。 a - b = a + (~b) + 1。我们也从小学就知道携带的概念。 9+3 = 2 携带一。在二进制中也一样,有两个操作数可以让 1 + 1 = 0 携带一个。所以逻辑中的每一列都需要一个进位。每个都是三位中的两位输出,两个操作数在加号中进位和进位并输出结果。由于这些逻辑块中的每一个都有一个输入位,进位,因此第一个进位是零的正常加法,但是对于减法,我们可以使该进位为 1 并反转第二个操作数以获得 a + b = a + (~ b) + 1

所以减法就是加法,如果你通过几个简单的例子,或者最好自己尝试操作数的每一个三位组合。您会看到,没有符号或无符号加法(或减法)之类的东西,二进制补码编码的美妙之处。

知道了这一切,减法就是加法,加法我们得到一个 UNSIGNED 溢出的进位,有符号溢出位是当 msbit 的进位和进位不匹配时,通常表示为 V 标志。现在有些体系结构,因为它们已经在输入时反转 b 操作数,在输入时将进位反转,所以它们在输出时反转进位。有些不。因此,您必须查看您的特定架构以了解进位是否被视为无符号加法溢出或借位。或者不借什么的。

零减去某些东西并不总是会进行加法。

0b000 - 0b111

 0001
  000
+ 000
=====
  001

加法的进位为零。您的架构可能会选择保持这种方式,也可能会选择颠倒它并称之为借用。

在一个建筑家族中。所有 x86 或所有 ARM 很可能会永远以同样的方式继续这样做。但是没有理由期望 ARM 和 MIPS 以及 x86 和 XYZ 都以同样的方式做到这一点。

从术语的角度来看,将其反转并将其定义为借用是有意义的。

请注意,所有(有符号/无符号)大于、小于、大于或等于、小于或等于定义都基于该架构的进位/借位选择,您无法跨架构转换这些标志比较,除非它们具有相同的定义。

【讨论】:

    【解决方案2】:

    进位标志是最高有效位(MSb)的进位或借位:

    CF (bit 0) Carry flag — 如果算术运算从结果的最高有效位产生进位或借位,则设置;否则清除。该标志表示溢出条件 无符号整数算术。它也用于多精度算术。

    不要将 CF 与符号位相关联,在减法中,只要被视为无符号的被减数小于被视为无符号的减数,就会设置 CF。
    这等效于溢出条件,对于有符号数,等效标志为 OF。

    对于一个(不必要的?)视觉线索,在这个 4-5 操作中,设置 CF 的是第二个借位,红色的借位

    如果你从零减去,很自然地,对于任何数字,除了零本身,你总是有 CF 集,因为减数至少有一个位集。


    最后,一些指令可以让您在不影响 CF 的情况下更改符号位(参见例如逻辑操作或 neg 的行为)。

    【讨论】:

    • 感谢您的详细回答。我的问题是我对借贷一无所知。我认为减法标志的设置方式与添加二进制补码的方式相同。但现在我意识到这是错误的。在 x86 架构中,进位标志设置为相反。 (子)4-5 = CF(1)。 (加) 4+(-5) = CF(0)。
    • @idlmn89:不要看有符号数的进位标志;溢出标志告诉你它们是否环绕。这对于所有架构上的进位标志都是正常的,AFAIK,而不仅仅是 x86。另见this very good carry vs. overflow tutorial。在扩展精度数据中,只有最高有效字包含符号位。其他单词实际上都是无符号的,因此您可以使用进位/借入和进位/借出来添加/替换它们。但是 MSB 只有进位,没有进位/借位。
    • “最后,一些指令可以让你在不影响 CF 的情况下更改符号位(参见例如逻辑运算或 neg 的行为)。” neg 确实有效进位标志。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多