Shop OBEX P1 Docs P2 Docs Learn Events
Algorithm debugging and floating point help — Parallax Forums

Algorithm debugging and floating point help

RussMRussM Posts: 27
edited 2013-03-22 03:51 in Propeller 1
Hey again everyone. I am having a program I am writing that uses two rotary encoders to measure size changes on a bicycle sprocket. In my code, everything works fine until the end where I want to print out the array of values in "float_highs". I've had trouble with displaying floating point values on my LCD screen in the past which makes me unsure if I have a problem with my algorithm itself or if it's just how I am utilizing the f32 functions. I have attached my code as well as a wonderfully drawn picture to help make my algorithm more clear. Thank you for any help!
 {{


┌──────────────────────────────────────────┐
│ Quadrature Decoder DEMO                  │
│ Author: Luke Haywas                      │
│                                          │
│                                          │
&#9474; Copyright (c) <2010> <Luke Haywas>       &#9474;               
&#9474; See end of file for terms of use.        &#9474;                
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;


Description:
Demonstrates how to use my object QuadDecoder.
Quadrature encoder is connected to pins 12 and 13.
(If connected to different pins, simply change the
value of ENCODER_PIN below)


Displays value of the accumulated variable in
the serial debug terminal.


}}


CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000
  '_xinfreq = 6_250_000
  
  TX_PIN = 0
  BAUD = 19_200
  MS_001 = CLK_FREQ / 1_000
  CLK_FREQ = ((_clkmode-xtal1)>>6)*_xinfreq
  
  LCD_PIN       = 27
  LCD_BAUD      = 19_200
  LCD_LINES     = 4
  LCD_COLS      = 20


  ENCODER_PIN_SIZE   = 5
  INDEX_PIN_SIZE = 6
  ENCODER_PIN_ANGLE = 1
  INDEX_PIX_ANGLE = 2


OBJ
  quad_size  :       "QuadDecoder"
  quad_angle  :       "QuadDecoder"
  LCD    :       "FullDuplexSerial"
  f32           : "F32"
  f32_orig      : "Float32Full"
  fs            : "FloatString"


VAR


  long offset_size                                          ' example variable that will be accumulated to
  long offset_angle
  long distance
  long temp
  long distance_tick
  long f1
  long fA
  long angle
  long angle_tick
  byte cog
  long stack[90]
  byte highs[200]
  byte lows[200]
  long future_size
  long current_size
  long previous_size
  long detecthighpoint
  long detectlowpoint
  long i
  long j
  long index
  long index2
  long float_highs[200]
  long float_lows[200] 
PUB main


  LCD.start(TX_PIN, TX_PIN, 00, 19_200)    'Initialize FullDuplexSerial.spin
  f32.start
  f32_orig.start 


  LCD.tx($0C)
  LCD.tx($11)
  
  quad_size.start(ENCODER_PIN_SIZE, @offset_size)                    ' start the size encoder
  quad_angle.start(ENCODER_PIN_ANGLE, @offset_angle)   
 




    LCD.str(string("Size ="))
    LCD.tx($0D)
    LCD.str(string("Angle ="))
    LCD.tx($0D)


    distance_tick := 0.000983
    distance := 0.0
    angle_tick := 0.144
    angle := 0.0
    current_size := 0.0
    previous_size := 0.0
    detecthighpoint := 0
    detectlowpoint := 1
    i := 0
    j := 0
    offset_size := 0.0                                         ' initialize the accumulator
    offset_angle := 0.0
    Index := 0
    
    repeat index2 from 0 to 199
      float_highs[index2] := 0.0


    repeat while (offset_angle < 2500)
      current_size := f32_orig.FMul(f32_orig.FFloat(offset_size), distance_tick)
      angle := f32_orig.FMul(f32_orig.FFloat(offset_angle), angle_tick)


      LCD.tx($87)
      LCD.str(fs.FloatToFormat(current_size,13,6))
      LCD.tx($9D)
      LCD.str(fs.FloatToFormat(angle,10,4))
         
      if (current_size < previous_size) AND (detecthighpoint == 1) '(previous size was a high)
        float_highs[i] := previous_size
        i++
        detectlowpoint := 1
        detecthighpoint := 0
      if (current_size > previous_size) AND (detectlowpoint == 1) '(previous size was a low)
        float_lows[i] := previous_size
        j++
        detecthighpoint := 1
        detectlowpoint := 0
           
        previous_size := distance               
        waitcnt(clkfreq/100 + cnt)


    LCD.tx($0C)  
    LCD.str(string("High Value = "))
    LCD.tx($0D)
    LCD.tx($0D)
    LCD.str(string("High Value = "))


    repeat index from 0 to i
      LCD.tx($94)                           
      LCD.str(fs.FloatToFormat(float_highs[i],13,6))
      LCD.tx($BC)
      LCD.str(fs.FloatToFormat(float_highs[i+1],13,6))
      pause(2000)


    LCD.tx($0C)
    quad_size.stop
    quad_angle.stop


PUB pause(ms) | t 
 t := cnt 
 repeat ms 
  waitcnt(t += MS_001)     






{{
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;&#61469;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570; TERMS OF USE: MIT License &#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61573;&#61570;&#61469;&#9474;                                                            
&#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
&#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
&#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
&#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
&#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
&#9474;                                                                                                                              &#9474;
&#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
&#9474;                                                                                                                              &#9474;
&#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
&#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
&#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
&#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
}}
1024 x 529 - 37K

Comments

  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-03-19 20:00
    One thing I noticed is you're using integer comparisons with floating point variables.
          if (current_size < previous_size) AND (detecthighpoint == 1) '(previous size was a high)
            float_highs[i] := previous_size
            i++
            detectlowpoint := 1
            detecthighpoint := 0
          if (current_size > previous_size) AND (detectlowpoint == 1) '(previous size was a low)
    
    

    The only comparisons that works with floats are "==" and "<>".

    You'll want to use the "Cmp" method of F32 to do your comparisons.
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-19 20:01
    repeat index from 0 to i
          LCD.tx($94)                           
          LCD.str(fs.FloatToFormat(float_highs[i],13,6))
          LCD.tx($BC)
          LCD.str(fs.FloatToFormat(float_highs[i+1],13,6))
          pause(2000)
    
    This looks supicious. You probably want to use float_highsCOLOR="#FF0000"]index[/COLOR and you should make sure that there actually is an element at index+1 (from the looks of it index i - the loop limit - points to the next free element).
  • ManAtWorkManAtWork Posts: 2,176
    edited 2013-03-20 03:28
    Duane Degn wrote: »
    One thing I noticed is you're using integer comparisons with floating point variables.
    The only comparisons that works with floats are "==" and "<>".
    You'll want to use the "Cmp" method of F32 to do your comparisons.

    Believe it or not, integer comparison does work for floating point variables. Sign is the most significant bit, then comes the exponent and the mantissa is stored in the low order bits. So even though the interpretation of the bits is different the order is correct. That means if floating point variable a is higher than b then the expression a > b for their integer representation is always true. I use this frequently in my code and never had problems.
  • RussMRussM Posts: 27
    edited 2013-03-20 06:16
    kuroneko wrote: »
    repeat index from 0 to i
          LCD.tx($94)                           
          LCD.str(fs.FloatToFormat(float_highs[i],13,6))
          LCD.tx($BC)
          LCD.str(fs.FloatToFormat(float_highs[i+1],13,6))
          pause(2000)
    
    This looks supicious. You probably want to use float_highsCOLOR=#FF0000]index[/COLOR and you should make sure that there actually is an element at index+1 (from the looks of it index i - the loop limit - points to the next free element).

    I am thinking it has something to do with my array of floating point values. As a test I tried filling the first 11 elements of floats_high with a random floating point value and simply printing it out to the screen. Alas, I still get a read out of 0.0000 for each value.
        index := 0
        
        repeat index2 from 0 to 10
          float_highs[index2] := 2342.32423
    
    
        LCD.tx($0C)  
        LCD.str(string("Value = "))
        LCD.tx($0D)
        LCD.tx($0D)
        LCD.str(string("Value = "))
    
    
    
    
        repeat index from 0 to (10)
          LCD.tx($94)                           
          LCD.str(fs.FloatToFormat(float_highs[index],13,6))
          LCD.tx($BC)
          LCD.str(fs.FloatToFormat(float_highs[index+1],13,6))
          pause(2000)
    

    EDIT: I replaced the FloatToFormat call with FloatToString and now it seems to print the values fine. However after making that change into my algorithm I am still getting odd results, so the error must be somewhere in my logic.
  • kuronekokuroneko Posts: 3,623
    edited 2013-03-20 06:33
    Looks like FloatToFormat has some trouble, FloatToString just works.
  • RussMRussM Posts: 27
    edited 2013-03-20 06:38
    kuroneko wrote: »
    Looks like FloatToFormat has some trouble, FloatToString just works.
    Yessir, I found that out the hard way! Atleast now I know it is an issue with my algorithm and not my formatting. Thank you for your help!
  • Heater.Heater. Posts: 21,230
    edited 2013-03-20 06:44
    Duane Deng.

    It's true. As noted above. Comparing floats using signed integer comparisons, less than, greater than, works just fine. Edit: NOT QUITE TRUE, see below in thread.

    I just wanted to add that this is not an accident. The designer of the IEEE 754 floating point standard. Professor William Kahan went to some effort to make sure that was the case. The idea being that if you can compare floats with integer ops that allows much faster sorting algorithms, like radix sort.
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-03-20 09:14
    Thanks ManAtWork and Heater for the education about floating points.

    I occasionally get PMs asking for help. I usually tell them I'd rather help in the forum so if I say something wrong I'll be corrected. I very much appreciate the correction.

    Now I wonder why the floating point objects have a "Cmp" method.
  • Heater.Heater. Posts: 21,230
    edited 2013-03-20 09:43
    Duang Degn,

    That's a good question.

    Presumably the cmp method returns -1, 0 or 1 depending how the comparison went. It's does start to look like a long winded way of doing it.
  • ersmithersmith Posts: 6,054
    edited 2013-03-20 10:24
    ManAtWork wrote: »
    Believe it or not, integer comparison does work for floating point variables.

    I think you'll find it only works if at least one variable is positive. When comparing two negative floating point numbers the integer comparison has to be reversed. For example, -1.0f is 0xbf800000, -2.0f is 0xc0000000; but while 0xbf800000 < 0xc0000000, -1.0 > -2.0.

    Eric
  • ersmithersmith Posts: 6,054
    edited 2013-03-20 10:26
    Duane Degn wrote: »
    Now I wonder why the floating point objects have a "Cmp" method.

    Besides the issue with comparing two negative numbers, there are also some corner cases (like infinities, NotANumbers, and -0) that also need to be considered when comparing floating point numbers.

    Eric
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-03-20 11:20
    ersmith wrote: »
    I think you'll find it only works if at least one variable is positive. When comparing two negative floating point numbers the integer comparison has to be reversed. For example, -1.0f is 0xbf800000, -2.0f is 0xc0000000; but while 0xbf800000 < 0xc0000000, -1.0 > -2.0.

    Eric
    ersmith wrote: »
    Besides the issue with comparing two negative numbers, there are also some corner cases (like infinities, NotANumbers, and -0) that also need to be considered when comparing floating point numbers.

    Eric

    Now I'm back to being confused. Which, I suppose, is better than being confidently wrong.

    Thanks for the continuing education Eric.
  • ersmithersmith Posts: 6,054
    edited 2013-03-20 11:54
    Duane Degn wrote: »
    Now I'm back to being confused. Which, I suppose, is better than being confidently wrong.

    :smile: Your original statement ("use the CMP method to compare floating point values") was correct, and is good advice. That's the way to compare floats. It's best to use CMP even for testing for equality or inequality. For example, the bit patterns 0x00000000 and 0x80000000 are different, but according to the IEEE standard they should compare as equal (they stand for 0.0 and -0.0, respectively). CMP handles this kind of thing.

    An even simpler way to use floating point is to do it in C rather than Spin, but that's a whole different topic :-).

    Eric
  • Heater.Heater. Posts: 21,230
    edited 2013-03-21 04:11
    ersmith,

    Dammit, you are right. You cannot compare floats using integer representation directly. But it can be done.

    My little mistake is that the integer representation of floats is ordered correctly provided you are treating your ints as signed magnitude format. Of course most computers now a days use two's complement so it only works if one or more of the floats is positive.

    What you have to do is convert your negative numbers to two's compliment and then compare them using normal signed integer operations. This is done by simple subtracting the negative numbers from hexadecimal 80000000. Might look like this in Spin.
    PUB compareFloats(x, y)
        ' Adjust floats x and y using signed integer operations.
        if x < 0
            x :=  $80000000 - x
        if y < 0
            y :=  $80000000 - y
        ' Now we can use < and > to make comparisons.
        ' Note: you can no longer use float ops on x and y.
        if x < y
            return -1
        if x > y
            return 1
        return 0
    

    As mentioned in this thread we may have problems here with NaNs and infinities, but really I think that is not an issue. If your code has such things cropping up in it you probably have bigger problems to deal with.

    WARNING: NEVER COMPARE FLOATS FOR EQUALITY.

    Sorry for shouting but this comes up a lot. In general floats are never equal when you might expect them to be. The classic example is this:

    (0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1) == (10 * 0.1)

    is false. Basically all to do with rounding errors. If you really want to understand the problems with comparing floats a good place to start is here:
    http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

    My old boss used to say to all new team members on a radar signal processing project "If you think you need floating point maths to solve the problem then you don't understand the problem."

    I always though he meant that you can do most things with fixed point arithmetic if you think about it enough and the floating point was just the lazy way out.

    Thirty years later I realize that his statement has another meaning, if you really do need floating point to solve your problem then you won't understand your problem any more! Reason being that floating point is hard and takes a lot of effort to understand all it's subtleties. As my error above shows:)
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-03-21 08:12
    Rather than hijacking this thread any more, I started another thread to ask about my patch to F32's ATan2 method. I'm hoping you guys can take a look at the other thread.

    Thanks,
  • Heater.Heater. Posts: 21,230
    edited 2013-03-22 03:51
    Just for a bit of completeness I should point out that the method used to convert floats to ints suitable for comparison with integer operations is the same as the procedure needed to convert those ints back to the original floats.
    'Convert float to lexicogrphically ordered twos-complemet int or vice-versa.
    'N.B. The int results can be used to compare floats using integer operations!
    PUB floatLexFloat (x)
            if x < 0
                    x = 0x80000000 - x
            return (x)
    
    
    So, if you happen to want to sort a bunch of floats then converting them to ints first will make it go faster and you can convert them back easily when done. I think it's kind of neat.
Sign In or Register to comment.