Shop OBEX P1 Docs P2 Docs Learn Events
Need help using Modulus to figure average — Parallax Forums

Need help using Modulus to figure average

Don MDon M Posts: 1,652
edited 2014-03-09 12:56 in Propeller 1
I know I read earlier that Modulus was used to get the remainder of a division equation. So I wanted to use it to figure an average of x events but I don't get the right answer.
  secs := 551                   ' Total events time
  term.dec(secs / 14)           ' Get average of 14 events
  term.tx(".")
  term.dec(secs // 14)


I was expecting my answer to be 39.4 (39.3571.... rounded up?) but it displays 39.5

What am I doing wrong?

Thanks.
Don

Comments

  • Heater.Heater. Posts: 21,230
    edited 2014-03-08 06:39
    14 goes into 551 39 times leaving a remainder of 5.
    So what you ordered is what you got.

    If you do it as
    secs := 551
    term.dec (secs / 14)
    term.dec (".")
    term.dec (secs * 100) / 14) // 100)
    

    You should get two decimal places.
  • Mark_TMark_T Posts: 1,981
    edited 2014-03-08 07:17
    Heater. wrote: »
    14 goes into 551 39 times leaving a remainder of 5.
    So what you ordered is what you got.

    If you do it as
    secs := 551
    term.dec (secs / 14)
    term.dec (".")
    term.dec (secs * 100) / 14) // 100)
    

    You should get two decimal places.

    More logical order would be:
    centiseconds := 551 * 100 / 14
    term.dec (centiseconds / 100)
    term.dec (".")
    term.dec (centiseconds / 10 // 10)
    term.dec (centiseconds // 10)
    

    You can then farm the printing to a separate routine as the divide by 14 isn't tangled up with the printing
    of the value. Note that each decimal digit is printed separately so that x.01 doesn't print as x.1
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-03-08 07:36
    Or you could multiply the seconds by 100 and use a method I call DecPoint to display it.

    I'll add a link in a minute or too to the method.
  • Don MDon M Posts: 1,652
    edited 2014-03-08 07:50
    Thanks Heater, Mak & Duane. I understand now.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-03-08 07:54
    There's a common technique when performing integer math to scale the integer to avoid rounding errors. I've also seen this referred to as "pseudo real numbers" in some literature (I think by Parallax).

    I usually have a constant I call "PSEUDO_MULTIPLIER" so I can easily change the amount the integer is scaled.

    In this case, I'll just use the magic number "100". You could use any other power of ten number (within reason) to scale the number. You do need to make sure your scaling number doesn't cause a 32-bit overflow.
    secs := 551
      scaledSecs := 551 * 100
      scaledAve := scaledSecs / 14
      DecPoint(scaledAve, 100)
    

    The above should display the seconds with two digits after the decimal point.
    PUB DecPoint(value, denominator)
      if value < 0
        Pst.Char("-")
        -value
          
      if value => denominator
        result := value / denominator
        Pst.Dec(result)
        value //= denominator     
      else    
        Pst.Char("0")
      Pst.Char(".")  
      repeat while denominator > 1
        denominator /= 10
        if value => denominator
          result := value / denominator
          Pst.Dec(result)
          value //= denominator
        else
          Pst.Char("0")
    

    The last significant digit is not rounded. A quick and dirty way to make sure the last digit is rounded is to add half of the dividing number to the original value. In this case you'd use:
    secs := 551
      scaledSecs := 551 * 100
      scaledAve := (scaledSecs + [COLOR=#ff0000]7[/COLOR])/ 14
      DecPoint(scaledAve, 100)
    

    The above should result in the number being correctly rounded. I haven't tested the code myself but I'm pretty sure it should work.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2014-03-08 07:59
    Here's my "DivideWithRound" method.
    PUB DivideWithRound(numerator, denominator)
    
    
      numerator += denominator / 2
      result := numerator / denominator
    
    
    
  • Heater.Heater. Posts: 21,230
    edited 2014-03-08 09:49
    Duane Degn,

    In the real world "pseudo real numbers" are known as "fixed point".

    Whilst a scale factor that is a power of ten is nice for what's gong on here remember other scale factors can be used. Powers of two are convenient because the scaling can be done with a fast shift operation instead of a slow multiply.
  • Duane C. JohnsonDuane C. Johnson Posts: 955
    edited 2014-03-09 12:56
    Heater. wrote: »
    14 goes into 551 39 times leaving a remainder of 5.
    So what you ordered is what you got.

    If you do it as
    secs := 551
    term.dec (secs / 14)
    term.dec (".")
    term.dec (secs * 100) / 14) // 100)
    
    You should get two decimal places.
    That is still not quite right.
    Floating divide of 551/14 = 39.3571428571429.
    So ite answer should be "39.36" not "39.35".
    Try this:
    secs := 551
    term.dec (secs / 14)
    term.dec (".")
    term.dec (secs * 100 * 2 + 1) / (14 * 2) // 100)
    
    This gives "39.36"

    These integer functions do "Truncations"
    By simply adding 0.5 to the value before thew truncation the answer is correct.
    In this case I essentially doubled the value and added 1 then divide by 2.

    Duane J
Sign In or Register to comment.