Shop OBEX P1 Docs P2 Docs Learn Events
f32 / floatstring question? — Parallax Forums

f32 / floatstring question?

Dwayne DibbleyDwayne Dibbley Posts: 63
edited 2011-08-08 13:18 in Propeller 1
i am trying to get the floating point math to work but with the following i would have expected to return 100 :

f1 := f32.FFloat(50)
f1 := f32.FMul( f1,2 )
term.str( fs.FloatToString( f1 ) )

but instead it returns 3.11653e-36?

am i doing something wrong again :)

Thanks

Comments

  • lonesocklonesock Posts: 917
    edited 2011-08-02 11:29
    You have the right idea. You can use FFloat to convert an integer to a floating point representation. If it's just a constant, the compiler can construct a FP constant if you use a decimal place. For example, in the FMul, you should really use 2.0 as the second parameter, that way the compiler stores it as a float. As written, the compiler stores the 2 as an integer.

    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-02 11:55
    thanks changing it to:

    f1 := f32.FFloat(50)
    f1 := f32.FMul( f1,2.0 )
    term.str( fs.FloatToString( f1 ) )

    resulted in 100 :)

    OK how can i convert a string eg "12.1234" in to a floating point to use with the math functions as FFloat only works with whole numbers :(

    Thanks
  • lonesocklonesock Posts: 917
    edited 2011-08-02 14:06
    Here, add this function to f32:
    PUB atof( strptr ) : int | f, sign, mag, dmag
      sign := 0
      mag := 0
      dmag := 0
      repeat
        case byte[strptr]
          "-":
              sign~~
          "0".."9":
              mag += dmag
              int := int*10 + byte[strptr] - "0"
          ".":
              dmag := -1
          other:    quit
        strptr++
    
      ' convert int to float
      if sign
        int := -int
      f := FFloat( int )
    
      ' do we need to modify the exponent?
      if (byte[strptr] == "E") or (byte[strptr] == "e")
        ' yep, read in the exp
        sign := 0
        int := 0
        repeat
          case byte[++strptr]
            "-":
                sign~~
            "0".."9":
                int := int*10 + byte[strptr] - "0"
            other:    quit
        if sign
          int := -int
        mag += int
    
      ' final answer?  scale by the magnitude
      return FMul( f, Exp10( FFloat( mag ) ) )
    
    If nobody finds any hideous bugs, I'll upload a version 1.4 with this functionality added.

    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-03 10:20
    works fine with whole numbers but when i feed f32.atof with 23.3760 i get returned by f32.floattostring 23.37591

    is this as expected?

    Thanks
  • lonesocklonesock Posts: 917
    edited 2011-08-03 10:29
    Unfortunately yes. The floating point packages (F32 included) use the built-in log/exp table for the appropriate functions, so there are rounding issues. I can make a version of atof that uses repeated divisions/multiplications by 10 instead of Exp10 to retain better precision. Ultimately the goal for F32 will be better precision, probably moving to a CORDIC routine (as I did for Sine & Cosine). That will be a bit tricky as I'm running low on cog space, but hey, challenges are fun! [8^) For now, give me a little while and I'll post the more precise (and a bit slower) code.

    thanks,
    Jonathan
  • lonesocklonesock Posts: 917
    edited 2011-08-03 10:40
    Here you go:
    PUB atof( strptr ) : f | int, sign, dmag, mag, get_exp, b
      ' get all the digits as if this is an integer (but track the exponent)
      ' int := sign := dmag := mag := get_exp := 0
      longfill( @int, 0, 5 )
      repeat
        case b := byte[strptr++]
          "-": sign := $8000_0000
          "+": ' just ignore, but allow
          "0".."9":
               int := int*10 + b - "0"
               mag += dmag
          ".": dmag := -1
          other: ' either done, or about to do exponent
               if get_exp
                 ' we just finished processing the exponent
                 if sign
                   int := -int
                 mag += int
                 quit
               else
                 ' convert int to a (signed) float
                 f := FFloat( int ) | sign
                 ' should we continue?
                 if (b == "E") or (b == "e")
                   ' int := sign := dmag := 0
                   longfill( @int, 0, 3 )
                   get_exp := 1
                 else
                   quit
      ' Exp10 is the weak link...uses the Log table in P1 ROM
      'f := FMul( f, Exp10( FFloat( mag ) ) )
      ' use these loops for more precision (slower for large exponents, positive or negative)
      repeat while mag > 0
        f := FMul( f, 10.0 )
        --mag
      repeat while mag < 0
        f := FMul( f, 0.1 )
        ++mag
    
    Jonathan
  • lonesocklonesock Posts: 917
    edited 2011-08-03 11:06
    Oops, here's a slightly more efficient version:
    PUB atof( strptr ) : f | int, sign, dmag, mag, get_exp, b
      ' get all the digits as if this is an integer (but track the exponent)
      ' int := sign := dmag := mag := get_exp := 0
      longfill( @int, 0, 5 )
      repeat
        case b := byte[strptr++]
          "-": sign := $8000_0000
          "+": ' just ignore, but allow
          "0".."9":
               int := int*10 + b - "0"
               mag += dmag
          ".": dmag := -1
          other: ' either done, or about to do exponent
               if get_exp
                 ' we just finished processing the exponent
                 if sign
                   int := -int
                 mag += int
                 quit
               else
                 ' convert int to a (signed) float
                 f := FFloat( int ) | sign
                 ' should we continue?
                 if (b == "E") or (b == "e")
                   ' int := sign := dmag := 0
                   longfill( @int, 0, 3 )
                   get_exp := 1
                 else
                   quit
      ' Exp10 is the weak link...uses the Log table in P1 ROM
      'f := FMul( f, Exp10( FFloat( mag ) ) )
      ' use these loops for more precision (slower for large exponents, positive or negative)
      b := 0.1
      if mag > 0
        b := 10.0
      repeat ||mag
        f := FMul( f, b )
    
    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-03 11:40
    Thanks Jonathan

    if i input 23.3784 i get 23.3784 returned :)
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-04 09:34
    now with the following:

    lat1 := f32.FAdd( f32.FDiv( f32.atof(gps.s_latm),60.0 ),f32.atof(gps.s_latd) )
    term.str( fs.FloatToString( lat1 )) 'lat1=50.38959
    term.tx(" ")
    term.str( fs.FloatToString( f32.ACOS(f32.COS(f32.RADIANS(90-lat1))))) 'returns 6.805647e+38

    in excel with =ACOS(COS(RADIANS(90-50.38959))) results in 0.691332073

    am i not using the f32 function calls correctly for this miss result?

    Thanks
  • lonesocklonesock Posts: 917
    edited 2011-08-04 10:00
    I'm guessing you want:
    f32.RADIANS(f32.FSub(90.0,lat1))
    
    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-04 10:03
    Sorry forgot the 90-lat1 should be f32.Sub(90.0,lat1)

    now comparing to excel

    excel results

    90-50.38959=39.61041
    RADIANS(39.61041)=0.691332
    COS(0.691332)=0.770397
    ACOS(0.770397)=0.691332

    f32 results

    90-50.38959=39.61031
    RADIANS(39.61031)=0.6913304
    COS(0.6913304)=0.7703919
    ACOS(0.7703919)=0.6913408

    is that as close as i am going to get with the current f32?

    Thanks
  • lonesocklonesock Posts: 917
    edited 2011-08-04 10:07
    Probably. Remember, Excel is using double precision for everything, F32 single. And the forward sine and cosine in F32 still use the (interpolated) propeller's ROM tables (the arc* functions use the more precise CORDIC routines). The next step for F32 is to CORDICify (please pretend that's a word [8^) the remaining trig and log/exp functions, so the accuracy should jump a bit then.

    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-04 10:17
    Thanks Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-04 12:09
    last one for this evening :)

    i have the following excel formula : =ACOS(F1*F2+F3*F4*F5) results in 0.065175656

    which i have tried to make a f32 version with : f32.ACOS(f32.FMul(F32.FMul(f32.FAdd(f32.FMul(f1,f2),f3),f4),f5)) results in 0.7385263

    but it gives a different result completely i would guess its must be down the the way i have used the f32 options ?

    Thanks again
  • lonesocklonesock Posts: 917
    edited 2011-08-04 12:17
    Given the order of operations in "ACOS(F1*F2+F3*F4*F5)", the addition needs to be the last operation performed before the ACOS (i.e. it needs to be the outermost function call, except for the ACOS). Something like this:
    f32.ACOS( f32.FAdd( FMul(f1,f2), f32.FMul( f32.FMul(f3,f4), f5)) )
    
    Jonathan

    Edit: last one for the evening? It's lunchtime here...feel free to keep those questions coming!
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-05 03:18
    That works fine Jonathan, now all the math is done the final calculation comes out at just over 1 mile out over a 288 mile distance ( calculating distance between two lat / lon points ) maybe im doing things the hard way.
    i am hopefully creating an gps tripmeter with 0.00 precision so my idea was to read the current gps position once a second and calculate the distance between them to generate a value for the tripmeter.

    Thanks

    UPDATE: would the 32 bit floating point coprocessor from parallax give me the double point precision?
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-05 10:48
    another idea how about getting the speed in knots from the gps and converting to Miles Per Hour then Multiplying by 1/10th sec hour ( GPS in outputting as 10hz )

    something like this:

    repeat
    dist := f32.FAdd(dist,f32.FMul(f32.FMul(f32.atof(string_with_gps_knots),0.868976242),0.0000277777778)) '0.868976242=knots to miles per hour & 0.0000277777778 =1/10 sec in hours
    term.str(fs.FloatToString(dist))

    would that work or should i multiply everything by 1_000_000 if too many decimal places first and devide the result I only need miles in 0.00 hundruths?

    Thanks
  • lonesocklonesock Posts: 917
    edited 2011-08-08 09:02
    One big thing to note about using floating point, is that 32-bit float numbers actually keep track of 24 bits of precision (23 actually are stored, the leading bit is always 1, thus doesn't need to be stored). That means ~7 decimal places in base 10. When you do operations like multiplication and division, you get to (basically) keep all of your significant figures, but when you add or subtract you can lose precision: For example, if I add 1,000,000 to 0.0001, both numbers obey the 7 significant figures rule, but to add them together the resulting number would have been 1,000,000.0001, which is way more than 7 sig-figs, so I lose the small portion. So, adding a bunch of small numbers (for example the distance traveled in 0.1 seconds) to a large number (say the total trip length in miles) will cause precision problems.

    Some possible solutions:
    - using integers...you have to control the scaling, but you get more digits to play with (google "fixed-point")
    - keep a variable "last_GPS", and keep checking the current position "current_GPS", and don't update the trip length until "dist_last_current" > threshold.
    (and a much more complex extension of the idea above: keep N intermediate trip_length variables. Always update the smallest trip length variable...and if that goes over a threshold then add it to the one above (this operation ripples "up" the chain). The thresholds might be 1, 10, 100, 1000, etc.)

    One other quick thing to note is multiplying a value by 2 consecutive constants can (should ;-) be merged, the compiler can do the math with constants. For example, "FMul( FMul( x, 0.1), 0.27 )" would be "FMul( x, 0.1 * 0.27 )".

    Jonathan
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-08 10:19
    Cheers Jonathan i knew i should have paid more attention in school :).

    so for the above example : dist := f32.FAdd(dist,f32.FMul(f32.FMul(f32.atof(string_wi th_gps_knots),0.868976242),0.0000277777778))

    should be like this : dist := f32.FAdd(dist,f32.FMul(f32.atof(string_wi th_gps_knots) * 0.868976242),0.0000277777778))

    and i can only use 7 sig-figs ie : 0.868976242 should be 0.868976 and i would be better multiplying 0.0000277777778 by 10_000 = 0.277777 so both values are in the same range? then dividing the result by something 10_000 ? to get back to the correct figure? and should generate better results ?

    Thanks again
  • Dwayne DibbleyDwayne Dibbley Posts: 63
    edited 2011-08-08 13:18
    noob alert

    formula should be knots / 0.8689762 and not * !!

    re ran a test run car odo read 11.2 Miles and propeller f32 read 11.13678 Miles with the following formula:

    dist := f32.FAdd(dist,f32.FMul(f32.FDiv(f32.atof(gps.s_speedk),0.8689762),0.00002777777))
Sign In or Register to comment.