汇编语言除法(问题是获取余数)

问题描述 投票:0回答:1
jmp start

  mess1 db 'Enter 1st number: $'
  mess2 db 0a,0d, 'Enter 2nd number: $'
  nextline db 0a,0d, '$'


start:

  mov ax, 03
  int 10h

  mov dx, offset mess1
  call printstring
  call input
  sub al, 30h
  push ax

  mov dx, offset mess2
  call printstring
  call input
  mov bl, al
  mov dx, offset nextline
  call printstring
  sub cl, 30h
  sub bl, 30h
  pop ax

  mov ah, 0
  aad
  div bl

  mov dl, al
  add dl, 30h


  push ax
  call printchar

  mov dl, '.'
  call printchar
  mov ah, 0
  pop ax
  mov dl, ah
  add dl, 30h
  call printchar
  int 20h


input:
  mov ah, 1
  int 21h
  ret

printstring:
  mov ah, 9
  int 21h
  ret

printchar:
  mov ah, 2
  int 21h
  ret

余数为0时有效,但只有当方程有余数时,余数才会显示错误的数字。

assembly x86-16 emu8086 integer-division
1个回答
0
投票

余数是分数的分子。例如,“5/3 = 1 余数为 2”(因为“5 - (1*3) = 2”),所以真实结果将是“1 + 2/3”(或“商 + 余数/除数”)。请注意,许多分数都是循环小数,“1 + 2/3”就是数字 2.666666666...

最简单的显示方式是将其显示为分数(例如打印余数、“/”符号和除数)。

将其打印为小数;您可以重复将“余数/除数”乘以 10 并提取整数部分。例如。 "2*10 / 3 = 6 with the remainder of 2" 所以你可以打印 '6' 然后用新的余数再做一次,对于这个例子来说是一样的,所以你最终会重复打印 '6' (直到您因其他原因停止 - 例如,因为您将其限制为 3 位数)。这对其他分数更有效,例如“1/8”;你会做“1 * 10/8 = 1余数为2; 2 * 10/8 = 2余数为4; 4 * 10/8 = 5余数为0”以获得数字1 , 2 然后 5.

另一个问题是,这将截断为零,人类更喜欢“舍入到最近”——例如,对于“5/3”,你最终会打印“5.666”,而人类会想要“5.667”。要解决这个问题,如果你在小数点后做 3 位数字,你想在开始时将 0.0005 添加到原始余数,然后用分数做它最终是交叉乘法 - 例如“余数/除数 + 1/2000 =(余数*2000)/(除数*2000) + 除数/(除数*2000) = (余数*2000 + 1)/(除数*2000)".

换句话说;如果在将分数转换为三位小数之前执行“remainder = remainder*2000 + 1”和“divisor = divisor * 2000”,您将得到正确舍入的结果。 注意:在某些情况下,此舍入可能会更改整数部分 - 例如值 3.99987654321 应该显示为“4.000”,但如果您不小心,您会显示为“3.000”。幸运的是,您不必为您的情况担心这一点(因为除数始终是 1 到 9 之间的数字)。

装配中;这可能看起来像(NASM 语法,未经测试):

; ax = number1, bl = number2

    div bl                 ;ah = remainder, al = quotient

    ;Do integer part

    mov dl,al              ;dl = quotient
    call printchar         ;Print the quotient

    ;Prepare for fractional part

    shr ax,8               ;ax = remainder (from 0 to 9)
    mov cx,2000
    mul cx                 ;dx:ax = ax = remainder*2000 (from 0 to 18000)
    lea dx,[ax+1]          ;dx = remainder*2000+1 (from 1 to 18001)
    mov ax,bx              ;ax = divisor (from 1 to 9)
    mul cx                 ;dx:ax = ax = divisor*2000 (from 2000 to 18000)
    mov bx,ax              ;bx = divisor*2000 (from 2000 to 18000)
    mov ax,dx              ;ax = remainder*2000+1 (from 1 to 18001)
    mov cx,10              ;cx = 10

    mov dl,'.'
    call printchar         ;Print the decimal point

    ;Do the first fractional digit

    mul cx                 ;dx:ax = (remainder*2000+1) * 10 (from 10 to 180010)
    div bx                 ;ax = (remainder*2000+1) / (divisor*2000); dx = next_remainder (from 0 to 18000)
    push dx
    mov dl,al
    call printchar         ;Print the first factional digit
    pop ax                 ;ax = next_remainder (from 0 to 18000)

    ;Do the second fractional digit

    mul cx                 ;dx:ax = next_remainder * 10 (from 10 to 180000)
    div bx                 ;ax = (next_remainder*10) / (divisor*2000); dx = next remainder
    push dx
    mov dl,al
    call printchar         ;Print the second factional digit
    pop ax                 ;ax = next_remainder (from 0 to 18000)

    ;Do the last fractional digit

    mul cx                 ;dx:ax = next_remainder * 10 (from 10 to 180000)
    div bx                 ;ax = (next_remainder*10) / (divisor*2000); dx = next remainder
    mov dl,al
    call printchar         ;Print the third factional digit

请注意,跟踪变量范围很重要(例如,正如我在上面的评论中所做的那样)以确保没有溢出错误。例如,一个从 10 到 180000 的整数值将不适合 16 位(并且不适合像 AX 这样的 16 位寄存器),我们只是幸运的是指令集使得使用一对对于这些情况,寄存器(“dx:ax”或“DX 中的最高 16 位和 AX 中的最低 16 位”)。

© www.soinside.com 2019 - 2024. All rights reserved.