gpt4 book ai didi

汇编语言 - 如何做模?

转载 作者:行者123 更新时间:2023-12-02 10:40:09 32 4
gpt4 key购买 nike

x86 汇编中是否有模运算符或指令之类的东西?

最佳答案

如果您的模数/除数是一个已知常数,并且您关心性能,请参阅 thisthis .对于直到运行时才知道的循环不变值,乘法逆甚至是可能的,例如见 https://libdivide.com/ (但如果没有 JIT 代码生成,这比仅硬编码一个常量所需的步骤效率低。)
从不使用 div对于 2 的已知幂:它比 and 慢得多求余数,或右移除法。查看 C 编译器输出以获取无符号或有符号除以 2 的幂的示例,例如on the Godbolt compiler explorer .如果您知道运行时输入是 2 的幂,请使用 lea eax, [esi-1] ; and eax, edi或类似的事情x & (y-1) . Modulo 256 更高效:movzx eax, cl在最近的 Intel CPU ( mov-elimination ) 上具有零延迟,只要这两个寄存器是分开的。

在简单/一般情况下:运行时未知值
DIV instruction (及其对应的 IDIV 对于有符号数)给出商和余数。对于无符号,余数和模数是一回事。已签名 idiv ,它给你 the remainder (not modulus)可以是负数:
例如-5 / 2 = -2 rem -1 . x86 除法语义完全匹配 C99 的 %运算符(operator)。DIV r32EDX: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
在 16 位汇编中你可以做 div bxDX:AX 中划分 32 位操作数来自 BX .参见英特尔的 Architectures Software Developer’s Manuals想要查询更多的信息。
通常总是使用 xor edx,edx未签名前 div 将 EAX 零扩展到 EDX:EAX。 这就是“普通”32 位/32 位=> 32 位除法的方式。
对于签名除法, 使用 cdq之前 idiv 将 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根据低半部分的符号位。 div/ 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,或者直接使用立即数。

如果商不适合一个寄存器,div 和 idiv 将出错 (AL/AX/EAX/RAX,与被除数宽度相同)。这包括除以零,但也会发生非零 EDX 和较小的除数。这就是 C 编译器只是零扩展或符号扩展而不是将 32 位值拆分为 DX:AX 的原因。
还有为什么 INT_MIN / -1是 C 未定义的行为:它溢出 2 的补码系统(如 x86)上的有符号商。见 Why does integer division by -1 (negative one) result in FPE?以 x86 与 ARM 的示例为例。 x86 idiv在这种情况下确实是错误的。
x86 异常是 #DE - 除法异常。在 Unix/Linux 系统上,内核向导致 #DE 异常的进程提供 SIGFPE 算术异常信号。 ( On which platforms does integer divide by zero trigger a floating point exception? )
对于 div ,使用 high_half < divisor 的红利是安全的。例如 0x11:23 / 0x12小于 0xff所以它适合 8 位商。
大数除以小数的扩展精度可以通过使用一个块的余数作为下一个块的高半被除数(EDX)来实现。这可能是他们选择余数=EDX 商=EAX 而不是相反的原因。

关于汇编语言 - 如何做模?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8021772/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com