1

当两个相乘的数字溢出寄存器时,如何使用这两个标志来正确计算答案?

例如,如果al保持0xff并乘以0x2,导致溢出到ax,标志如何帮助解决这个问题?

4

2 回答 2

3

使用单操作数形式时,x86/x64 上的乘法永远不会溢出。
这是因为mul及其兄弟imul产生的输出是其操作数1的两倍。
在您的示例中,乘以al产生输出ax并且不会产生溢出。

当结果不适合操作数大小时设置CFand 。 这可用于执行饱和乘法,例如:OF

;Unsigned

mul ebx
sbb edx, edx      ;EDX = CF replicated along all the 32 bits
or eax, edx       ;EAX = 0ff..ffh if overflow, EAX*EBX otherwise

;Signed (perhaps not the most efficient way)

imul ebx
cmovc eax, 7fffffffh  ;Signed positive max if overflow.   (CMOV-immediate doesn't really exist, but imagine register sources)
cmovnc edx, 0         ; don't modify EAX for the non-overflow case.
sar   edx, 31         ; EDX = all-ones if overflow && negative
xor   eax, edx        ; if negative && overflow
                      ;    flip 7fffffff (INT_MIN) to 80000000 (INT_MIN)
                      ; else xor with 0 is a no-op

(当前的 Rust 编译器也a.saturating_mul(b)使用 imul 的 FLAGS 实现 i32 和 i64,但设置不同:https ://rust.godbolt.org/z/ab3jMjzbv )


然而,要实现多精度乘法,比如 64x64 位,不需要这些标志,事实上,用 2 32k表示我们有:

(a· k +b) × (c· k +d) = a·c· k 2 + (a·d+b·c)· k + b·d

其中 32 位产品产生 64 位结果,添加如下

.----.----.
|   b·d   |
'----'----'
       +
     .----.----.
     | a·d+b·c |
     '----'----'
            +
          .----.----.
          |   a·c   |
          '----'----'

 =

.----.----.----.----.
|    128-bit result |
'----'----'----'----' 

1这足以防止溢出。

于 2016-12-17T00:44:14.930 回答
1

回答此类问题的最佳方法是阅读手册。

现在我要回到我的 80188/186 纸质副本(我不知道我的 8088/8086 编程手册在哪里)。但即使在那时,就像人们所说的那样。文字说

如果源是字节,则乘以寄存器 AL,在 AH 和 AL 中返回双倍长度结果。

这一切都很好:正如人们所说,你不能溢出,但人们通常不会编写使用两倍于操作数大小的结果的高级语言代码。它进一步说:

如果结果的上半部分为非零,则设置 CF 和 OF;否则他们会被清除。

所以你去吧:如果你正在做一个 8 位操作并且结果不适合 8 位 (0xFF * 2 = 0x1FE),那么 CF 和 OF 都被设置。至少,他们是 20 年前。做一个实验来看看你当前的 x86 做了什么应该很简单。您可以在发布到这里之前制作这个实验,浏览每个操作数组合,看看标志对每个组合有什么作用。

于 2016-12-17T10:04:27.817 回答