News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Finding the verage rounded to the nearest integer

Started by infoMASM, February 06, 2014, 04:59:33 PM

Previous topic - Next topic

infoMASM

I am trying to write code that takes the sum of the integers entered in by the user divided by the number of integers entered and finds the average rounded to the nearest integer for those numbers.
Example:
entries: 100, 59, 0, 0, 0, 0, 0, 0
159/8 = 19.8 (This should round to 20)

How can I do this? Any help?
Thank you!


INCLUDE Irvine32.inc
.data

myMessage BYTE "Assignment #3 Created by Justin Bruntmyer",0dh,0ah,0
introMssg BYTE "Welcome to the best Integer Accumulator!",0dh,0ah,0
askName BYTE "What is your name?",0dh,0ah,0
greetMssg BYTE "Nice to meet you ",0
greetMssg2 BYTE ", you are awesome!",0dh,0ah,0
instructMssg BYTE "Please enter numbers less than or equal to 100 and enter",0
instructMssg2 BYTE " a negative number whne you are finished to get the results",0dh,0ah,0
askNum BYTE "Enter a number: ",0dh,0ah,0
numEntered BYTE "You entered ",0
numEntered2 BYTE " numbers.",0dh,0ah,0
sumMssg BYTE "The sum of your numbers is ",0dh,0ah,0
avgMssg BYTE "The rounded average is ",0
errorMssg BYTE "ERROR: Enter a number that is less than or equal to 100 please.",0dh,0ah,0
userName BYTE 33 DUP(0)
exitMssg BYTE "Thank you for using this Integer Accumulator ",0
exitMssg2 BYTE ", have a great day!"

number DWORD ?
total DWORD ?
quantity DWORD ?
sum DWORD ?
average DWORD ?
numTerms  DWORD ?
remainder DWORD ?
newRem DWORD ?

.code

main PROC

call Clrscr
mov edx, OFFSET myMessage
call WriteString
mov edx, OFFSET introMssg
call WriteString
call Crlf

mov edx, OFFSET askName
call WriteString
mov edx, OFFSET userName
mov ecx, 32
call ReadString
call Crlf

mov edx, OFFSET greetMssg
call WriteString
mov edx, OFFSET userName
call WriteString
mov edx, OFFSET greetMssg2
call WriteString
call Crlf

mov edx, OFFSET instructMssg
call WriteString
mov edx, OFFSET instructMssg2
call WriteString
call Crlf

askLoop:
mov edx, OFFSET askNum
call WriteString
call ReadInt
;check if number is greater than or equal to 100
cmp eax, 100
jg falseSection
cmp eax, 0
jl finished
;if the number makes it past these thencalc, save and restart
inc numTerms
jmp calcSection

falseSection:
mov edx, OFFSET errorMssg
call WriteString
jmp askLoop

calcSection:
;summation
mov number, eax
mov eax, total
add eax, number
mov total, eax

;average
mov edx, 0
mov eax, total
mov ebx, numTerms
div ebx
mov average, eax
mov remainder, edx

cmp average, edx
add average, 1
cmp ebx, average
sub average, 1
jmp askLoop

finished:
mov edx, OFFSET sumMssg
call WriteString
mov eax, total
call WriteDec
call Crlf

mov edx, OFFSET numEntered
call WriteString
mov eax, numTerms
call WriteDec
mov edx, OFFSET numEntered2
call WriteString
call Crlf

mov edx, OFFSET avgMssg
call WriteString
mov eax, average
call WriteDec
call Crlf

mov edx, OFFSET exitMssg
call WriteString
mov edx, OFFSET userName
call WriteString
mov edx, OFFSET exitMssg2
call WriteString
call Crlf


exit
main ENDP

END main


jj2007

Hi,
One way is to simply add one and then continue with integer arithmetics:

  mov eax, 159
  mov ecx, 8

  inc eax
  cdq   ; look it up in opcodes.hlp - sets edx to zero in this case
  div ecx

Another way is to use the FPU:
  mov eax, 159
  mov ecx, 8

  push eax
  fild dword ptr [esp]
  mov dword ptr [esp], ecx
  fidiv dword ptr [esp]
  fistp dword ptr [esp]
  pop eax

Compare the results for sum=165...

infoMASM

I see that works for 159/8 but it needs to be able to round anything to a whole integer. 165/8=20.6 so should round to 21 but doesn't  :icon_confused:

TWell

Is this enought ?

mov eax, 165
mov ecx, 8
cdq   ; look it up in opcodes.hlp - sets edx to zero in this case
div ecx
shr ecx,1 ; half of divisor
.if edx > ecx ; remainder bigger than half of divisor ?
inc eax
.endif

MichaelW

If the FPU is set to use the default rounding mode, round to nearest or to even if equidistant, it should round the integer result of 165/8 to 21.

;==============================================================================
include \masm32\include\masm32rt.inc
.686
;==============================================================================
.data
    r8 REAL8 ?
.code
;==============================================================================
start:
;==============================================================================
    push 165
    fild DWORD PTR [esp]
    push 8
    fild DWORD PTR [esp]
    add esp, 8
    fdiv
    fst r8
    printf("%f\n",r8)
    push eax
    fistp DWORD PTR [esp]
    pop eax
    printf("%d\n\n",eax)
    inkey
    exit
;==============================================================================
end start


20.625000
21


You can return the FPU to the default mode with FINIT.

Round_half_to_even
Well Microsoft, here's another nice mess you've gotten us into.

Gunther

Hi infoMASM,

Michael did explain it right. The Operating System starts the FPU in the rounding mode ROUND to NEAREST. You can use the following alternatives:

Round a double value to an integer:

        cvtsd2si eax, xmm0             ; round xmm0 to eax


Round a single value to an integer:

        cvtss2si eax, xmm0             ; round xmm0 to eax


That should work.

Gunther
You have to know the facts before you can distort them.

jj2007

#7
Quote from: TWell on February 06, 2014, 07:52:18 PM
Is this enough ?

That's a clever solution indeed :t
To get the ".5 rounds up" rule, two minor changes:

  inc ecx
  shr ecx,1      ; half of divisor
  .if edx >= ecx      ; remainder equal or bigger than half of divisor ?
      inc eax
  .endif


Same logic but shorter and faster, and applicable to all kinds of divisors:

   div ecx
   add edx, edx
   .if edx>=divisor   ; remainder equal or bigger than half of divisor ?
      inc eax
   .endif

dedndave

#8
add the numbers up into 2 dword variables (dividend), keep the count in another dword variable (divisor)
    add     dwLoDividend,EnteredValue
    inc     dwDivisor         ;INC does not affect the carry flag
    adc     dwHiDividend,0


now, divide
    mov     eax,dwLoDividend
    mov     edx,dwHiDividend
    mov     ecx,dwDivisor
    div     ecx


EAX holds the quotient and EDX holds the modulus (often called the remainder)
the modulus will always be smaller than the divisor
because the modulus and divisor are likely to be small in this case,
we can double either one of them without overflowing the register
if the divisor is large (> 2147483647), we would have to divide the divisor by 2, instead

compare the doubled modulus with the divisor and round to nearest
    shl     edx,1             ;EDX = 2*modulus
    cmp     edx,ecx           ;carry flag = 1 if 2*modulus less than divisor
    sbb     eax,-1


SBB (subtract with borrow) does all the work for you   :P

EDIT: if there is a possibility that EAX will overflow from 0FFFFFFFFh to 0....
you can add another line to adjust
    sbb     eax,0

infoMASM

Thank you guys so much!! All of this info is great and is very useful!! Thank you for showing me how the FPU and all the other information as well!!

Thanks again!!