在x86汇编中是否有类似于模运算符或指令的东西?
在x86汇编中是否有类似于模运算符或指令的东西?
div
来处理2的幂次方:与其余数相比,它要慢得多,或者使用右移来进行除法。查看C编译器输出以获取2的幂次方的有符号或无符号除法示例,例如在Godbolt编译器资源管理器上。如果您知道运行时输入是2的幂次方,请使用lea eax,[esi-1]
;and eax,edi
或类似的方法来执行x & (y-1)
。模256更有效率:movzx eax,cl
在最近的英特尔CPU上具有零延迟(mov-elimination),只要两个寄存器是分开的即可。
DIV
指令(以及其有符号数的对应项IDIV
)同时给出商和余数。对于无符号数,余数和模数是相同的。对于带符号的idiv
,它会给出余数(而不是模数),这可能是负数:
例如 -5 / 2 = -2 rem -1
。x86除法语义与C99的%
运算符完全匹配。
DIV r32
将EDX:EAX
中的64位数字除以32位操作数(在任何寄存器或内存中),并将商存储在EAX
中,余数存储在EDX
中。如果商溢出,则会导致错误。
无符号32位示例(适用于任何模式)
mov eax, 1234 ; dividend low half
mov edx, 0 ; dividend high half = 0. prefer xor edx,edx
mov ebx, 10 ; divisor can be any register or memory
div ebx ; Divides 1234 by 10.
; EDX = 4 = 1234 % 10 remainder
; EAX = 123 = 1234 / 10 quotient
div bx
来通过DX:AX
的32位操作数除以BX
。更多信息请参见Intel的Architectures Software Developer’s Manuals。div
,在运算前应始终使用xor edx,edx
将EAX零扩展到EDX:EAX。这是执行“正常” 32位/32位 => 32位除法的方法。
对于带符号的除法,在使用idiv
之前要使用cdq
进行符号扩展,将EAX扩展为EDX:EAX。另请参见Why should EDX be 0 before using the DIV instruction?。对于其他操作数大小,请使用cbw
(AL->AX)、cwd
(AX->DX:AX)、cdq
(EAX->EDX:EAX)或cqo
(RAX->RDX:RAX),根据低半部分的符号位将高半部分设置为0
或-1
。
idiv
可用于8、16、32位操作数,以及(在64位模式下)64位操作数。在当前的Intel CPU上,64位操作数比32位或更小的操作数慢得多,但是AMD CPU只关心数字的实际大小,而不关心操作数的大小。请注意,8位操作数是特殊的:隐式输入/输出在AH:AL(又名AX)中,而不是DL:AL。有关示例,请参见8086 assembly on DOSBox: Bug with idiv instruction?。
带符号64位除法示例(需要64位模式)
mov rax, 0x8000000000000000 ; INT64_MIN = -9223372036854775808
mov ecx, 10 ; implicit zero-extension is fine for positive numbers
cqo ; sign-extend into RDX, in this case = -1 = 0xFF...FF
idiv rcx
; quotient = RAX = -922337203685477580 = 0xf333333333333334
; remainder = RDX = -8 = 0xfffffffffffffff8
div dword 10
无法编码成机器码(因此汇编器会报告有关无效操作数的错误)。
与mul
/imul
不同(在这种情况下,您通常应该使用更快的2操作数imul r32,r/m32
或3操作数imul r32,r/m32,imm8/32
,而不浪费时间写入高半位结果),没有新的操作码用于除以立即数,或者32位/32位=>32位除法或余数而没有高半位被除数输入。
除法非常缓慢且(希望)很少使用,他们没有费心添加一种让您避免使用EAX和EDX或直接使用立即数的方法。
这也是为什么在2的补码系统(如x86)上,INT_MIN / -1
是C未定义行为:它会导致带符号商溢出。请参见为什么整数除以-1(负一)会导致FPE?,了解x86与ARM的示例。在这种情况下,x86的idiv
指令确实会出错。
x86异常是#DE
- 除法异常。在Unix/Linux系统上,内核向导致#DE异常的进程发送SIGFPE算术异常信号。 (在哪些平台上,整数除以零会触发浮点异常?)
对于div
,使用具有high_half < divisor
的被除数是安全的。例如,0x11:23 / 0x12
小于0xff
,因此适合于8位商。
-2 // 5
为-1
,-2 %% 5
为3
(它们一起满足a %% b == a - ((a // b) * b)
),而使用截断的除法和余数,我们有-2 / 5
为0
,-2 % 5
为-2
(它们一起满足a % b == a - ((a / b) * b)
)。 - Aaron Frankea % b == a & (b - 1)
。AND EAX, 63
,因为63在二进制中表示为111111。b=0
呢? - LetsGoBrandonb=0
时未定义。 - Nirvana如果您不太关心性能并希望使用简单直接的方法,您可以使用DIV
或IDIV
。
DIV
或IDIV
只接受一个操作数,其中它将某个寄存器与此操作数进行除法运算,操作数只能是寄存器或内存位置。
当操作数为字节时: AL = AL / 操作数,AH = 余数(模数)。
例如:
MOV AL,31h ; Al = 31h
DIV BL ; Al(商)= 08h,Ah(余数)= 01h
当操作数为字时: AX = (AX) / 操作数,DX = 余数(模数)。
例如:
MOV AX,9031h ; Ax = 9031h
DIV BX ; Ax=1808h & Dx(余数)= 01h