【问题标题】:x86 Assembly - idiv to get decimal placesx86 程序集 - idiv 获取小数位
【发布时间】:2016-09-08 21:10:23
【问题描述】:

我仅限于在当前进程中使用 .386。使用 intel x86 如何获得除法的小数位?基本上我希望它输出到百分之一:12.81

这是我当前的代码:

mov AX, Dividend
cwd
idiv Divisor

outputW AX ;Outputs Quotient (whole number)
outputW DX ;Outputs Remainder

我尝试先将 AX 乘以 100,但这对我仍然不起作用。 我目前也仅限于制作变量DividendDivisor 字号。我尝试用cwde 命令替换cwd,但这对我不起作用。

任何帮助将不胜感激!

谢谢

【问题讨论】:

  • 取余数乘以 100 再除以原始除数。您必须调整它以进行舍入。
  • 如果您在 32 位环境中,您可以考虑使用 32 位寄存器,如 EAX 代替 AXEDX 而不是 DX.
  • @MichaelPetch 谢谢你的帮助。现在我将如何将 AX 变量移动到 EAX 中,以便我可以使用整个 EAX?我对如何去做这件事感到困惑。
  • 在比 286 更新的处理器上,您希望将 16 位值移动到 32 位寄存器并对其进行零扩展,您可以使用 movzxmovzx eax, word ptr [Dividend] 之类的东西你可以用 movsx 做符号扩展。我也可能误解了你在问什么。因为你提到了.386 指令,所以我假设 MASM/TASM 语法。
  • 是的,我相信。到目前为止,一切都运行得更好,我终于可以看到进展了。完成 EAX 寄存器后,如何将值移回 AX 变量?我试过 movsx Quotient(Word Size), dword ptr [EAX] 似乎没有编译

标签: assembly x86 integer-division


【解决方案1】:

您可以在执行除法之前将被除数乘以 100,调整四舍五入,然后再除以 100 以获得小数点后两位。由于您的初始代码是 16 位的,因此这里先提供一个 16 位的解决方案:

    ;; Perform the initial division (100 * Dividend / Divisor)
    MOV AX, Dividend    ; Load the Dividend into AX
    IMUL OneHundred     ; DX:AX = AX * 100
    IDIV Divisor        ; DX:AX / Divisor... AX=Quotient, DX=Remainder

    ;; Handle the remainder
    SHL DX, 1           ; DX now equals the remainder times two
    CMP DX, Divisor     ; Compare Remainder*2 to the Divisor
    JL l1               ; If Remainder*2 is <, we round down (nothing changes)
    INC AX              ; If Remainder*2 is >=, we round up (increment the quotient)
l1:

    ;; Divide by 100 again to get the final result
    CWD                 ; Sign-extend AX to DX:AX
    IDIV OneHundred     ; DX:AX / 100... AX=Quotient, DX=Remainder
    MOV IntegerPart, AX ; Now AX is the integer part
    MOV DecimalPart DX  ; And DX is the fractional part

OneHundred DW 100

或者,如果 32 位解决方案适用,您可以改用 32 位整数:

    ;; Perform the initial division (100 * Dividend / Divisor)
    MOVSX EAX, Dividend ; Sign-extend the Dividend into EAX
    IMUL OneHundred     ; EDX:EAX = EAX * 100
    MOVSX ECX, Divisor  ; Sign-extend the Divisor into ECX
    IDIV ECX            ; EDX:EAX / Divisor... EAX=Quotient, EDX=Remainder

    ;; Handle the remainder
    SHL EDX, 1          ; EDX now equals the remainder times two
    CMP EDX, ECX        ; Compare Remainder*2 to the Divisor
    JL l1               ; If Remainder*2 is <, we round down (nothing changes)
    INC EAX             ; If Remainder*2 is >=, we round up (increment the quotient)
l1:

    ;; Divide by 100 again to get the final result
    CDQ                 ; Sign-extend EAX to EDX:EAX
    IDIV OneHundred     ; EDX:EAX / 100... EAX=Quotient, EDX=Remainder
    MOV IntegerPart, EAX ; Now EAX is the integer part
    MOV DecimalPart EDX  ; And EDX is the fractional part

OneHundred DD 100

注意:最好使用 32 位代码,因为它不会从大商中生成 Divide Errors。考虑 16 位情况下的 Dividend=32767Divisor=1

  • 第一个乘法:DX:AX = 32767*100 = 3276700
  • 第一个除法:DX:AX / 1 = 3276700 / 1 = 3276700(余数为零),但3276700 太大而无法容纳AX(大于32767),因此产生除法错误。

这不会发生在 32 位上,因为 3276700 可以放入 EAX。 (当然,除以零在任何一种情况下都会产生错误)

另外说明:此代码假设初始除法的结果为正

【讨论】:

  • 如何将其输出到包含小数点的控制台?由于填充的 0,我不断获得大量空间。 OneHundred DD 100 是什么意思?另外,您可以将其更改为签名号码吗?我看到你提到的初始除法的结果必须是积极的。
  • 在 32 位版本中,我可能更喜欢 imul eax, eax, 100 / cdq,避免需要 OneHundred。 (如果您愿意,这也可以让您将符号扩展的除数保留在 eax 以外的寄存器中)。当然,您也需要 100 作为第二个 idiv 的操作数。 mov ecx, 100 / idiv ecx 更像是编译器会发出的内容(使用-Os;否则它将用乘以模逆和一些移位代替常数除法。)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-23
  • 1970-01-01
  • 2012-05-11
  • 2012-02-07
  • 2023-03-14
  • 2023-03-30
相关资源
最近更新 更多