当两个相乘的数字溢出寄存器时,如何使用这两个标志来正确计算答案?
例如,如果al
保持0xff
并乘以0x2
,导致溢出到ax
,标志如何帮助解决这个问题?
使用单操作数形式时,x86/x64 上的乘法永远不会溢出。
这是因为mul及其兄弟imul产生的输出是其操作数1的两倍。
在您的示例中,乘以al
产生输出ax
并且不会产生溢出。
当结果不适合操作数大小时设置CF
and 。
这可用于执行饱和乘法,例如: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 32和k表示我们有:
(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这足以防止溢出。
回答此类问题的最佳方法是阅读手册。
现在我要回到我的 80188/186 纸质副本(我不知道我的 8088/8086 编程手册在哪里)。但即使在那时,就像人们所说的那样。文字说
如果源是字节,则乘以寄存器 AL,在 AH 和 AL 中返回双倍长度结果。
这一切都很好:正如人们所说,你不能溢出,但人们通常不会编写使用两倍于操作数大小的结果的高级语言代码。它进一步说:
如果结果的上半部分为非零,则设置 CF 和 OF;否则他们会被清除。
所以你去吧:如果你正在做一个 8 位操作并且结果不适合 8 位 (0xFF * 2 = 0x1FE),那么 CF 和 OF 都被设置。至少,他们是 20 年前。做一个实验来看看你当前的 x86 做了什么应该很简单。您可以在发布到这里之前制作这个实验,浏览每个操作数组合,看看标志对每个组合有什么作用。