Shop OBEX P1 Docs P2 Docs Learn Events
'Pre' vs Post-increment question. — Parallax Forums

'Pre' vs Post-increment question.

I assumed the difference between pre-increment and post-increment was not a big deal.
The following code increments 0 to 3
PUB Load_Packets(a)  | idx

    load_up[idx] := a
    idx := ++idx//4
    if idx == 3
      TransmitString
This code returns only zero. Why?
PUB Load_Packets(a)  | idx

    load_up[idx] := a
    idx := idx++//4
    if idx == 3
      TransmitString

Comments

  • evanhevanh Posts: 15,917
    edited 2016-04-05 21:05
    I guess it's a case of pre/post-increments being considered as an independent operation - outside of the general equation. So you have a situation where idx is being written twice for a single equation.

    In the case of the first equation it gets written sequentially (in-order) and therefore acts as expected, but in the second equation gets written concurrently (out-of-order) with the second write being the one that counts. The second write will come from the modulus operation which will always have zero as its input in-turn because it always wrote zero as the second writer.
  • evanhevanh Posts: 15,917
    edited 2016-04-05 21:20
    Err, not quite right either. Same result but what's probably more accurate is the first equation succeeds not due to any write ordering but rather that the pre-increment is used by the subsequent modulus operation and thereby gets applied to the second write operation. The first write is irrelevant in both equations.

    If the equation result didn't go back to idx then you wouldn't have a problem.
  • evanh wrote: »
    The second write will come from the modulus operation which will always have zero as its input in-turn because it always wrote zero as the second writer.
    I see it now. Thanks.
    idx := idx++//4
    
    The idx++ has to wait until the next iteration though the loop before it increments. I just tested idx := 1 which only returns 1.
  • You shouldn't use post-increment with an assignment of the same variable because they basically fight with each other.

    Pre-increment is fine because the increment happens first, THEN the usage in the expression. It actually turns into something like this:
    preIncIdx := idx +1
    idx := (preIncIdx) //4
    

    In the second case, the code is probably computing the post-incremented version of idx "off to the side", like this:
    postIncIdx := idx+1      'compute the post-increment result
    resultIdx := idx // 4    'compute the expression result
    idx := postIncIdx        'assign the post-increment result
    idx := resultIdx         'assign the result of the expression
    

    You're essentially causing an order-of-operations snafu in the expression evaluator by telling it to assign a value AND increment a value after the expression.
  • JonnyMacJonnyMac Posts: 9,105
    edited 2016-04-06 14:18
    The // operater is expensive in terms of time because it uses division. I do simple roll-overs and roll-unders like this:
      if (++idx == 4)
        idx := 0
    
      if (--idx < 0)
        idx := 3
    

    As Jason points out, you can easily create conflicts with these operators so I do the idx adjustment after its required use.

    There is a problem with snippet:
    PUB Load_Packets(a)  | idx
    
        load_up[idx] := a
        idx := idx++//4
        if idx == 3
          TransmitString
    

    The variable idx is local hence temporary -- you need a global variable (that maintains its value) for this code to work the way you want. I would re-structure like this.
    pub load_packet(a)
    
      load_up[idx] := a
      if (++idx == 4)
        transmit_string
        idx := 0
    

    My guess is that idx is the length of the packet; after four values you want to transmit -- this seems a bit for obvious to me.

  • lardomlardom Posts: 1,659
    edited 2016-04-06 17:46
    JonnyMac wrote: »
    The // operater is expensive in terms of time because it uses division. I do simple roll-overs and roll-unders like this:
      if (++idx == 4)
        idx := 0
    
      if (--idx < 0)
        idx := 3
    

    pub load_packet(a)
    
      load_up[idx] := a
      if (++idx == 4)
        transmit_string
        idx := 0
    

    My guess is that idx is the length of the packet; after four values you want to transmit -- this seems a bit for obvious to me.

    I will make the change. 'Avoid division when possible.' Execution speed does matter in this phase of my project.
    BTW, I'd like to understand some of the ways a 4-byte-string differs from 4 byte variables that I can put in a parameter list. I want to control 4 servos wirelessly. If I transmit a byte string from DAT the Parallax Serial Terminal interprets it properly.
    I can avoid a parameter list if I use cognew and pass a variable address to a child object but I can't figure out how to do the same wirelessly. I don't want anyone to have to wade too deep into the weeds for my project but I am puzzled.
  • Your particular case could be handled by using the & operator as well. If you want the remainder of a division by any number that is a power of two, it's the same as using a bitwise AND operation (&) of the divisor minus one, and the & operation is MUCH faster (a single PASM instruction).

    So:
    X // 2 is the same as X & 1
    X // 8 is the same as X & 7
    X // 256 is the same as X & 255

    Beware that this only holds true for positive numbers though - The remainder of a division for negative numbers will be negative, whereas using the & trick will always give a positive result. That can be a useful side effect though - In JonnyMac's case, his roll over / roll under cases would both be handled by the same instruction:
    ++idx
    idx &= 3
    
    ..or..
    
    --idx
    idx &= 3
    
Sign In or Register to comment.