Ali
Ali

Reputation: 19

MASM - Rounding Integers

In my program I'm trying to calculate the average of two integers, and then round the number. For example if the average is -22.5, it will round to -23. validInputs is the number of valid inputs gathered from the user and will always be a positive integer, while validSum will always be a negative integer as it only adds negative integers. However when comparing the remainder, I am not getting the correctly rounded result, and was wondering why.

Edit: I made the suggested change to -5 but am still experiencing the same error. I believe my error must be a logical problem.

mov     eax, validSum
cdq
mov     ebx, validInputs
idiv    ebx
mov     validAverage, eax
mov     intRemainder, edx
cmp     edx, -5 ; compare the remainder to -5
jg      display ; if greater than -5 we know not to round/decrement
dec     validAverage

Upvotes: 1

Views: 3402

Answers (1)

rkhb
rkhb

Reputation: 14409

If you just divide by 2 the solution is easy. IDIV gives you in this case only three possible remainders: {-1,0.1} which means:

  • -1: the sum was negative, the number behind the decimal point is -0.5, the average has to be subtracted by 1, i.e. avg = avg + (-1).

  • 0: no remainder, the average is already correctly rounded, i.e. avg = avg + 0.

  • 1: the sum was positiv, the remainder caused 0.5 and has to be added by 1, i.e. avg = avg +1.

As you can see, there's no need for a long-winded comparison, you can just add the remainder EDX to EAX:

; Only valid for EBX=2
mov     eax, validSum
cdq                                 ; EAX -> EDX:EAX
mov     ebx, validInputs
idiv    ebx                         ; EDX:EAX / EBX -> EAX remainder EDX
mov     validAverage, eax           ; Save result
add     validAverage, edx           ; Add -1 or 0 or 1
jmp     display                     ; End of Rounding

Besides that, the remainder has nothing to do with a fractional part of a decimal number. It is simply wrong, to compare it directly with 5 or -5. You must first get the first fractional digit. In a long division you would "add" a 0 at the end and then divide by the divisor. The same here: Multiply by ten (= add 0 at the end) and divide by EBX. Now you can divide the result by 5 to get the triple as above: {-1,0.1}

...
idiv    ebx                         ; EDX:EAX/EBX
mov     validAverage, eax           ; Save result

mov eax, edx
add eax, eax                        ; A quick way to multiply by 10
lea eax, [eax + eax * 4]            ;    EAX * 10 = EAX * 2 + EAX * 8
cdq                                 ; EAX -> EDX:EAX
idiv ebx                            ; EDX:EAX / EBX -> EAX remainder EDX
mov  ebx, 5
cdq                                 ; EAX -> EDX:EAX
idiv ebx
add validAverage, eax               ; Add -1 or 0 or 1
jmp     display                     ; End of Rounding

You can transform EAX*10/EBX/5 into EAX*2/EBX:

mov     eax, validSum
cdq                                 ; EAX -> EDX:EAX
mov     ebx, validInputs
idiv    ebx                         ; EDX:EAX / EBX -> EAX remainder EDX
mov     validAverage, eax           ; Save result

imul eax, edx, 2                    ; EAX = EDX * 2
cdq                                 ; EAX -> EDX:EAX
idiv ebx                            ; EDX:EAX / EBX
add validAverage, eax               ; Add -1 or 0 or 1

jmp display                         ; End of Rounding

Upvotes: 3

Related Questions