Shop OBEX P1 Docs P2 Docs Learn Events
Bad Math Formula solved — Parallax Forums

Bad Math Formula solved

PliersPliers Posts: 280
edited 2012-05-27 05:04 in Propeller 1
This line gives unexpected results:
dataAverage := ((dataAverage+DataIn)/b)

I can get the desired relsults using this
temp :=((temp + DataIn) )
dataAverage := (temp/b)

Any insights?
CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
OBJ                              
serial: "fullduplexserial"     ' Communication to Parallax Serial Terminal 
Var
word DataIn
word temp
word DataAverage
word b 
pub DataAveraging
serial.Start(31,30,0, 9600)


waitcnt((clkfreq*4) +cnt)   'Time to start up the terminal.
b:=1
temp:= 0 
DataIn:=7
DataAverage:= 0
      
 repeat while b < 11
    'dataAverage := ((dataAverage+DataIn)/b) ' Bad Math Formula
    temp :=((temp + DataIn) )   ' working formula.
    dataAverage := (temp/b)     ' working formula.
    serial.dec(b)            'b value sent to the terminal.   
    serial.str(string(" "))   'Space sent to the terminal.    
    serial.dec( DataAverage)   'DataAverage sent to the terminal.
    serial.str(string(" "))    'Space sent to the terminal.   
    serial.dec( DataIn)        'DataIn sent to the terminal.
    serial.str(string(" "))    'Space sent to the terminal. 
    serial.tx(13)              ' New line           
    waitcnt((clkfreq/2) +cnt)
          b:=b+1
       

Comments

  • RaymanRayman Posts: 15,367
    edited 2012-05-24 13:24
    I don't see the problem right away, so it's probably something obvious :smile:...

    But, you might have better luck with variables defined as "long" instead of "word".
    I think most things run better as long. I only use words for big arrays of things to save space...
  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-05-24 13:30
    In your "bad" formula, the previous value of dataAverage is used which is less then the sum of all the data.

    The first loop dataAverage (DA) would equal 7 using the first equation, The second time through the loop would it would also equal 7, but the third time through the loop DA would equal 4 ((7+7)/3).

    Your second method of finding the average doesn't have this problem.
  • pedwardpedward Posts: 1,642
    edited 2012-05-24 13:45
    Duane is right, however I'll touch on another gotcha that happens: underflow/overflow of variables.

    Sometimes the intermediate values in an expression will be too small to represent properly as a single expression and you need to rewrite the expression to maintain precision in the intermediate variables.

    Often times it's easier to read and enforce a particular evaluation order by breaking the expression up into multiple lines. In SPIN, what you see is what you get, there is no trick optimization to get in the way,
    however in C you can run into optimizations that will break how an expression is evaluated unless you break it into multiple expressions that are evaluated in a specific order.
  • PliersPliers Posts: 280
    edited 2012-05-24 16:31
    Thanks for the insight Duane. It does explain the odd results I was getting.
    I thought I knew basic algebra.



    Here is how I thought it was going to go.
    I renamed the values in the formula for an easier discussion.
    X starts at zero and C starts at 1.

    Repeat the following
    X=(X+7)/C
    C = C+1
    Loop back

    This is what I thought was going to happen.
    The first time through, x=7 because: of this equation… . X=(0+7)/1. Not this x=(0/1 + 7/1)
    The second time through, x=7 because: of this equation…. X=(7+7)/2. Not this x=(7/2 + 7/2)
    The third time through, x=7 because: of this equation… . X=(14+7)/3. Not this x=(14/3 + 7/3)
    The fourth time through, x=7 because: of this equation… . X=(21+7)/4. Not this x=(21/3 + 7/4)
    This would certainly take in to account the unexpected results I saw.
  • pedwardpedward Posts: 1,642
    edited 2012-05-24 16:42
    Here is the relevant section of the Propeller manual dealing with your issues (pg 145-146):
    Level of Precedence
    Each operator has an assigned level of precedence that determines when it will take action in
    relation to other operators within the same expression. For example, it is commonly known
    that Algebraic rules require multiply and divide operations to be performed before add and
    subtract operations. The multiply and divide operators are said to have a “higher level of
    precedence” than add and subtract. Additionally, multiply and divide are commutable; both
    are on the same precedence level, so their operations result in the same value regardless of the
    order it is performed (multiply first, then divide, or vice versa). Commutative operators are
    always evaluated left to right except where parentheses override that rule.
    The Propeller chip applies the order-of-operations rules as does Algebra: expressions are
    evaluated left-to-right, except where parentheses and differing levels of precedence exist.
    Following these rules, the Propeller will evaluate:

    X = 20 + 8 * 4 – 6 / 2

    ...to be equal to 49; that is, 8 * 4 = 32, 6 / 2 = 3, and 20 + 32 – 3 = 49. If you wish the
    expression to be evaluated differently, use parentheses to enclose the necessary portions of
    the expression.
    For example:

    X = (20 + 8) * 4 – 6 / 2

    This will evaluate the expression in parentheses first, the 20 + 8, causing the expression to
    now result in 109, instead of 49.
    Table 2-10 indicates each operator’s level of precedence from highest (level 0) to lowest
    (level 12). Operators with a higher precedence are performed before operators of a lower
    precedence; multiply before add, absolute before multiply, etc. The only exception is if
    parentheses are included; they override every precedence level.
    Intermediate Assignments
    The Propeller chip’s expression engine allows for, and processes, assignment operators at
    intermediate stages. This is called “intermediate assignments” and it can be used to perform
    complex calculations in less code. For example, the following equation relies heavily on X,
    and X + 1.

    X := X - 3 * (X + 1) / ||(X + 1)

    The same statement could be rewritten, taking advantage of the intermediate assignment
    property of the increment operator:

    X := X++ - 3 * X / ||X

    Assuming X started out at -5, both of these statements evaluate to -2, and both store that value
    in X when done. The second statement, however, does it by relying on an intermediate
    assignment (the X++ part) in order to simplify the rest of the statement. The Increment
    operator ‘++’ is evaluated first (highest precedence) and increments X’s -5 to -4. Since this is
    a “post increment” (see Increment, pre- or post- ‘+ +’, page 152) it first returns X’s original
    value, -5, to the expression and then writes the new value, -4, to X. So, the “X++ - 3...” part
    of the expression becomes “-5 – 3...” Then the absolute, multiply, and divide operators are
    evaluated, but the value of X has been changed, so they use the new value, -4, for their
    operations:
    -5 – 3 * -4 / ||-4 → -5 – 3 * -4 / 4 → -5 – 3 * -1 → -5 – -3 = -2
    Occasionally, the use of intermediate assignments can compress multiple lines of expressions
    into a single expression, resulting in slightly smaller code size and slightly faster execution.
  • PliersPliers Posts: 280
    edited 2012-05-25 04:28
    Thanks for your interest and time concerning my understanding of the spin language.
    I did read that section in the manual (a few times). In my manual (version 1.0), that chapter is on page 252. It is the same text.

    I still don't see where the parenthesis have a lower precedent compared to of an addition or subtraction operation.
    expression to be evaluated differently, use parentheses to enclose the necessary portions of
    the expression.
    For example:

    X = (20 + 8) * 4 – 6 / 2

    Any how... it is what it is.

    Is there a way to write " X=(X+7)/C " in one line?
    X is suppose to equal the summation of "X+7" before dividing by C.

    I see in an earlier post I switched to "Z" as the divider. I will edit that post.
  • Mike GMike G Posts: 2,702
    edited 2012-05-25 07:40
    This is not a math problem. This is a logic issue.
    X starts at zero and C starts at 1.

    Repeat the following
    X=(X+7)/C
    C = C+1
    Loop back

    The loop producing the following results.

    x=(0+7)/1 = 7
    x=(7+7)/2 = 7
    x=(7+7)/3 = 4
    x=(4+7)/4 = 2
    x=(2+7)/5 = 1

    This will produce the expected results from post #5
      repeat i from 1 to 10
        x :=  (i*7) / c++
    

    If you are trying to do an average with and unknown number of samples then you must accumulate the the total in x and divid by the number of iterations. If you know the number of samples the math is

    x += s/#ofSamples

    Be careful with the later equation. This is integer based math - so - x must be significantly larger than #ofSamples. If x is close in value to #ofSamples then multiply x buy some value 10^y.
  • PliersPliers Posts: 280
    edited 2012-05-25 07:52
    Thanks Mike for your input.
    Yes, I seem to have a logic problem.
    Oh well, I'll get over it.

    But until then....

    x=(1+7)/1 = 7. x evaluates correctly (in my mind).
    x=(7+7)/2 = 14. x evaluates correctly (in my mind). Again.
    x=(7+7)/3 = 4. but why is this not (14+7)?
    x=(4+7)/4 = 2. can see where the 4+7 came from , because the previous result was 4.
    x=(2+7)/5 = 1. I can see where the 2+7 came from , because the previous result was 2.


    I'll look at the loop again and try seeing it differently.

  • Duane DegnDuane Degn Posts: 10,588
    edited 2012-05-25 08:47
    Pliers,

    Mike just made a few math errors (or typos) while explaining your logic error. I wonder what kind of error I'm making as I explain this?
  • PliersPliers Posts: 280
    edited 2012-05-25 10:00
    Duh, now I see it. face slap.

    Again, I really do appreciate your input.
    Very best regards.


  • Mike GMike G Posts: 2,702
    edited 2012-05-25 13:04
    Updated post 5 - sorry - should have copied the terminal screen.
  • pedwardpedward Posts: 1,642
    edited 2012-05-25 13:21
    samples = samples + 1
    avg = (avg + sample) / samples
  • John AbshierJohn Abshier Posts: 1,116
    edited 2012-05-25 20:12
    Input is 10, 10, 10
    avg = (0 + 10) / 1 = 10
    avg = (10 + 10) / 2 = 10
    avg = (10 + 10) / 3 = 6.67?

    John Abshier
  • pedwardpedward Posts: 1,642
    edited 2012-05-26 17:17
    Sorry for posting something dumb.

    An average can be simple:

    a = (a+b) / 2

    or weighted:

    a = ((a * c) + b) / (c + 1)

    Breaking it down for larger values of a or c:

    a = a * c
    a = a + b
    a = a / (c+1)

    The weighted average has the advantage that outliers won't dramatically change the averaged value, so the average represents data over c samples.

    The simple average has the advantage that it doesn't require much arithmetic or large variable sizes:

    add a, b
    shr a,#1
  • Duane C. JohnsonDuane C. Johnson Posts: 955
    edited 2012-05-27 05:04
    Input is 10, 10, 10
    avg = (0 + 10) / 1 = 10
    avg = (10 + 10) / 2 = 10
    avg = (10 + 10) / 3 = 6.67?

    John Abshier

    Generally the third answer would be 6 because we usually are working with integer numbers.

    OK, we could use a floating point object and get 6.66666....

    Or using integer math avg = (10 + 10) * 1000/ 3 = 666

    Duane J
Sign In or Register to comment.