我对imul
在以下指令中设置OF
的原因感到困惑
mov al, 48
mov bl, 4
imul bl ; AX = 00C0h, OF = 1
但NOT在以下指令中
mov ax, 48
mov bx, 4
imul bx ; DX:AX = 000000C0h, OF = 0
IMUL
在结果的下半部分(解释为适当大小的有符号整数(不等于两个输入数字相乘的数学结果时设置溢出标志。
48乘以4的数学结果是192。在imul bl
之后留在AL中的值是C0h
。作为一个有符号的8位整数,表示数字-64。从数学上讲,192和-64不是同一个数字,所以必须设置溢出。
在imul bx
之后,AX中剩下的值是00C0h
。作为一个有符号的16位整数,表示数字192。因此没有设置溢出标志。
等效地,如果符号扩展到下半部分将产生与完整乘积不同的值,则设置溢出标志。当您将8位值C0h
进行符号扩展到16位时,您将得到FFC0h
,它不等于00C0h
,因此设置了溢出。当您将16位值00C0h
进行符号扩展到32位时,您将得到000000C0h
,它与实际的32位结果相等,因此不会设置溢出。
等效地,只有当结果的高半部分的每一位都等于低半部分的符号位时,才清除溢出标志。8位值C0h
的符号位为1,但高半部分不是FFh,而是00h,因此设置了溢出。16位值00C0h
的符号位为0,高半部分为0000h
,因此溢出被清除。
OF
告诉您是否可以在不更改其值的情况下将双宽度完整结果截断回输入宽度。(当被解释为imul
1的2补整数时,mul
的无符号二进制整数。(0xC0
是负8位数字的位模式,但0x00C0
是正16位数字。
如果您使用imul
或mul
作为其他8位或16位操作(如add
、(序列的一部分,而没有计划使用结果的高半部分,那么这就是您想要知道的
(或者,如果窄版本足够快,值得进行分支,则可以退回到可以处理更宽尺寸的扩展精度版本。(
如果你已经打算使用输出的全宽,不要检查of/CF;加宽乘法不能在丢失任何输出位的意义上溢出。两个N比特数的乘积总是可以用2N个比特来表示。
脚注1:包括2和3操作数形式,如imul cx, dx, 123
,它们不会在任何位置写入高半部分。通常情况下,您会将该形式用于非加宽的有符号或无符号数学,但如果您想检测无符号乘法何时换行,最好只使用mul
,即使这会迫使您使用EAX并以EDX:EAX形式输出。参见C和asm-中的imulq和无符号长-长溢出检测