Shop OBEX P1 Docs P2 Docs Learn Events
String to Float — Parallax Forums

String to Float

Sniper KingSniper King Posts: 221
edited 2008-12-09 16:42 in Propeller 1
I may have posed this question before but I don't think I got an answer.·

I have a string (GPS) and I want to convert this to a floating value.· i can't find anything that converts a string pointer to a floating value.· I wrote a small program to do this but it stops at 4 decimal places.

PUB Str2Float(ptr) :c | a ,i , B ,k
a:=1
c:=0
  
a:=fp.ffloat(a)
    
 
  c:=fp.ffloat(c) 
  repeat i from 0 to strsize(ptr)-1
     
    
      if byte[noparse][[/noparse]ptr+i]=="."
         
       quit
    if byte[noparse][[/noparse]ptr+i]<>"-"
     a:=fp.fdiv(a,10.0)    
   
 a:=fp.fmul(a,10.0)
   k:=0
  repeat i from 0 to strsize(ptr)
    b:=0.0
   if byte[noparse][[/noparse]ptr+i]=="-"
      k:=1
    
   if byte[noparse][[/noparse]ptr+i]=="1"
    b:=fp.fdiv(1.0,a)
   if byte[noparse][[/noparse]ptr+i]=="2"
    b:=fp.fdiv(2.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="3"
    b:=fp.fdiv(3.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="4"
    b:=fp.fdiv(4.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="5"
    b:=fp.fdiv(5.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="6"
    b:=fp.fdiv(6.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="7"
    b:=fp.fdiv(7.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="8"
    b:=fp.fdiv(8.0,a) 
   if byte[noparse][[/noparse]ptr+i]=="9"
    b:=fp.fdiv(9.0,a) 
     
   if byte[noparse][[/noparse]ptr+i]<>"." and byte[noparse][[/noparse]ptr+i]<>"-"
       a:=fp.fmul(a,10.0)
   C:=fp.fadd(c,b)
   debug.str(fs.floattostring(a))
    debug.str(string(" - "))
   debug.str(fs.floattostring(c))
   debug.str(string(13))
   
    
 if k==1
       c:=fp.fneg(c)


▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
·- Ouch, thats not suppose to be hot!··


Michael King
Application Engineer
R&D
Digital Technology Group

Comments

  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-09-11 17:58
    The reason it hasn't been done is it's not easy to do, the problem is there are so many different ways to provide a legal floating point format (17.5, 1.75E1, 175e-1, 0.000175e5, and the list is infinite), you must be able to handle anything thrown at it. Also you have to handle the integer portion seperate from the fractional portion (which itself is a pain), keep track of where the decimal place is, then where the·binary decimal place is, then finally deal with the exponent. You must also check for a valid number, since some numbers entered may not be representable in single precision float.

    I have been working on this for the last couple of days, but it is low man on the totem pole as far as priorities. Here is where the code is so far, but it's not complete and hasn't been tested.

    CON
        NaN  = $7FFFFFFF
        Infp = $7F800000
        Infn = $FF800000
        
    PUB StringToFloat(Str): float_n | int, frac, fraccnt, infrac, msign, expon, esign, inexpon, i
      inexpon := false
      'expon := mantcnt := exponcnt := 0
      repeat i from 0 to strsize(Str)
        case BYTE[noparse][[/noparse]Str][noparse][[/noparse]i]
          "e", "E" :  inexpon := true                       'done processing mantissa, move to exponent
          "-" : if inexpon                                  'check if in exponent
                  if expon == 0                             'check if this is the first digit of exponent
                    esign := 1                              'if yes indicate exponent is negative
                  else
                    return NaN                              'text is in a wrong format
                elseif int == 0 and frac == 0               'if - is in mantissa, make sure it's before any numbers  
                  msign := 1                                'indicate mantissa is negative
                else
                  return NaN                                'invalid placement of -
          "+" :                                             'do nothing
          "0".."9" : if inexpon                             'add to exponent
                       expon *= 10
                       expon += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0"
                     elseif infrac                          'add to fraction portion of mantissa
                       frac *= 10
                       frac += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0"
                       fraccnt++
                     else                                   'add to integer portion of mantissa
                       int *= 10
                       int += BYTE[noparse][[/noparse]Str][noparse][[/noparse]i] - "0"
          "." : if inexpon
                  return NaN                                'decimal points not allowed in exponent
                else
                  infrac := true                            'we're in the fraction
          OTHER: return NaN                               'invalid character in string
                  
      i := lookupz(fraccnt: 0, 10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000) 'overflow value for fraction
      fraccnt := 0                                          'reset fraction count to count binary positions
      if i                                                  'if i <> 0 ie a fraction exists
        repeat while frac                                   'repeat until frac == 0
          fraccnt++                                         'increment binary digit count
          frac <<= 1
          int <-= 1                                         'rotate integer
          if int & 1                                        'too many digits
            int ->= 1                                       'rotate back
            quit                                            'quit from repeat loop
          if frac > i                                       'overflow occured
            int++                                           'so set fractional bit to 1
            frac -= i                                       'subtract out overflow value to get remaining fraction
     
    'To be done: calculate binary exponent, range check exponent and mantissa, assemble final
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.

    Post Edited (Paul Baker (Parallax)) : 9/11/2008 6:09:15 PM GMT
  • Sniper KingSniper King Posts: 221
    edited 2008-09-11 18:12
    Can you explain ...
    BYTE[noparse][[/noparse]Str][noparse][[/noparse]i]
    

    I have seen this but I don't know how it works.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ·- Ouch, thats not suppose to be hot!··


    Michael King
    Application Engineer
    R&D
    Digital Technology Group
  • Mike GreenMike Green Posts: 23,101
    edited 2008-09-11 18:13
    Here's a simpler version that neither handles an explicit exponent nor does any real error checking
    ' Here's a simple example of a string to float routine.
    ' It accepts an optional leading "+" or "-" sign followed
    ' by a string of digits with an optional decimal point.
    ' The value of the string of digits (ignoring the decimal
    ' point) must fit into a 32 bit long.  There's no error
    ' checking for this.  The number of significant digits
    ' after the decimal point must be less than 15 and there
    ' is no error checking for this either.
    
    ' This code fragment assumes that one of the floating
    ' point objects has been included in this program under
    ' the name "flt".
    
    PRI stringToFloat(str) | sign, char, exp
       exp := 0
       ' Check for an optional sign
       if (sign := byte[noparse][[/noparse]str] == "-") or byte[noparse][[/noparse]str] == "+"
          str++
       ' Accumulate the value of the digits before the decimal
       repeat while byte[noparse][[/noparse]str] => "0" and byte[noparse][[/noparse]str] =< "9"
          result := result * 10 + (byte[noparse][[/noparse]str++] - "0")
       ' If there's a decimal point, accumulate those digits too
       if byte[noparse][[/noparse]str++] == "."
          repeat while byte[noparse][[/noparse]str] => "0" and byte[noparse][[/noparse]str] =< "9"
             result := result * 10 + (byte[noparse][[/noparse]str++] - "0")
             exp++   ' Count the # digits after the decimal
       else
          str--
       ' Here could be the code to check for and handle an exponent
       ' Now convert the integer to floating point and compensate
       ' for the number of digits after the decimal point.
       result := flt.fDiv(flt.fFloat(result),powers[noparse][[/noparse]exp])
       if sign   ' Include the sign
          result := flt.fNeg(result)
    
    DAT
    powers  long  1.0E00, 1.0E01, 1.0E02, 1.0E03, 1.0E04
            long  1.0E05, 1.0E06, 1.0E07, 1.0E08, 1.0E09
            long  1.0E10, 1.0E11, 1.0E12, 1.0E13, 1.0E14
    
  • Paul BakerPaul Baker Posts: 6,351
    edited 2008-09-11 18:22
    Sniper King said...
    Can you explain ...
    BYTE[noparse][[/noparse]Str][noparse][[/noparse]i]
    

    I have seen this but I don't know how it works.

    It is the way for picking out a specific character out of a string, iow return the BYTE value located at address Str, offset i.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Paul Baker
    Propeller Applications Engineer

    Parallax, Inc.
  • Sniper KingSniper King Posts: 221
    edited 2008-09-11 18:26
    Is this the same as
    BYTE[noparse][[/noparse]Str+i]
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ·- Ouch, thats not suppose to be hot!··


    Michael King
    Application Engineer
    R&D
    Digital Technology Group
  • Sniper KingSniper King Posts: 221
    edited 2008-09-11 18:30
    this is what I am getting From Mike's script...

    31.48293
    -110.246

    It seems to me i should be getting a few more places before it goes dumb

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ·- Ouch, thats not suppose to be hot!··


    Michael King
    Application Engineer
    R&D
    Digital Technology Group
  • Mike GreenMike Green Posts: 23,101
    edited 2008-09-11 18:39
    The stringToFloat routine uses 31 bits to accumulate the number. That should give you about 9 digits of accuracy.
    The IEEE floating point format allows 23 bits for a mantissa which only provides for slightly less than 7 digits of accuracy.
    I don't know how you're getting the numbers you're showing in your message, but you should get a little more accuracy.
  • Sniper KingSniper King Posts: 221
    edited 2008-09-11 18:44
    Ok, get this

    Here is my output

    String we are converting to a floating point value= -110.246233

    Floatstring setprecision=7

    Multiplier···· ·· Value· multiplied······· New value from Floatstring
    ················shown from floatString····

    100····················· 1··························100

    10······················ ·1························ ·110

    0······················· ·0························ ·110

    .1······················ ·.2······················· ·110.2

    .01······················ .04······················ ·110.24

    .001···················· .005···················· ·110.245

    .0001················ · .0009················· 110.2459

    .00001·············· ·.00007················ 110.246

    .000001············ ·.000006··············· 110.246

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    ·- Ouch, thats not suppose to be hot!··


    Michael King
    Application Engineer
    R&D
    Digital Technology Group
  • Mike GreenMike Green Posts: 23,101
    edited 2008-09-11 18:56
    You can't use something like FloatString to make judgements about the exact floating point value that you have.
    There are all sorts of assumptions and round-off errors inherent in that, particularly when you have just slightly
    less than 7 significant decimal digits of accuracy. If you want to check the accuracy, you'll have to hand convert
    the floating point value (or write your own conversion program) by keeping it in binary for as long as possible,
    applying the binary exponent, then converting first the integer portion to decimal, then the fraction portion.

    With a value on the order of 110, you will only get between 4 and 5 decimal places. That's all the floating point
    routines are capable of providing. If you need more, you may have to create your own arithmetic package.

    Post Edited (Mike Green) : 9/11/2008 7:01:18 PM GMT
  • AribaAriba Posts: 2,687
    edited 2008-09-11 19:39
    Here is my version of a String to float conversion (it also does not support e and E exponents):
    PUB StrToFloat(strptr) : flt | int,exp,sign
      int := exp := sign := 0
      repeat strsize(strptr)                   'string to integer
        case byte[noparse][[/noparse]strptr]
          "-":      sign~~
          ".":      exp := 1
          "0".."9": int := int*10 + byte[noparse][[/noparse]strptr] - "0"
                    if exp
                      exp++                    'count dec places
          other:    quit
        strptr++
      if sign
        int := -int
      flt := f.FFloat(int)
      if exp
        repeat exp-1
          flt := f.FDiv(flt,10.0)              'adjust float
    
    


    You need a floating point object included as f.

    See also the thread here: http://forums.parallax.com/showthread.php?p=738380

    Andy
  • AleAle Posts: 2,363
    edited 2008-09-12 12:40
    Mike Green said...

    With a value on the order of 110, you will only get between 4 and 5 decimal places. That's all the floating point
    routines are capable of providing. If you need more, you may have to create your own arithmetic package.

    I disagree: That's all the float32 package is able to provide. There are other implementations around: the 12 digit BCD package and the double precision package (sqrt comes soon) that I described at the propeller.wikispaces.com/MATH

    Conversion routines and spin wrappers are not provided, I use them from assembler wink.gif, but can be easily created.
  • jazzedjazzed Posts: 11,803
    edited 2008-09-13 02:12
    Maybe someone should get Parallax to add a StrToFloat routine in FloatStrings.spin.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    --Steve
  • s2jesses2jesse Posts: 62
    edited 2008-09-14 07:16
    I asked this question before as well... Ariba·code worked perfectly for my needs.. thanks again.

    ·
  • Jay KickliterJay Kickliter Posts: 446
    edited 2008-12-09 14:42
    Ariba's float to string method is working fine for me, but I am getting some strange results after that. Since this thread was about converting GPS to floating point, I'm posting here.

    PUB mainLoop | MMmm, dddd, latitudeDDdd, latitudeStrPointer
      repeat
        latitudeStrPointer := gps.latitude
        MMmm := strToFloat(latitudeStrPointer + 2)
        dddd := floatM.fdiv(MMmm, 60)
        latitudeDDdd := floatM.fadd(40, dddd))
        uarts.str(debug_port, latitudeStrPointer)
        uarts.tx(debug_port, COMMA)
        uarts.str(debug_port, floatStr.floatToString(MMmm))
        uarts.tx(debug_port, COMMA)
        uarts.str(debug_port, floatStr.floatToString(dddd))
        uarts.tx(debug_port, COMMA)
        uarts.str(debug_port, floatStr.floatToString(latitudeDDdd))
        uarts.tx(debug_port, CR)
    
    PUB StrToFloat(strptr) : flt | int,exp,sign
      int := exp := sign := 0
      repeat strsize(strptr)                   'string to integer
        case byte[noparse][[/noparse]strptr]
          "-":      sign~~
          ".":      exp := 1
          "0".."9": int := int*10 + byte[noparse][[/noparse]strptr] - "0"
                    if exp
                      exp++                    'count dec places
          other:    quit
        strptr++
      if sign
        int := -int
      flt := floatM.FFloat(int)
      if exp
        repeat exp-1
          flt := floatM.FDiv(flt,10.0)              'adjust float  
    
    



    is resulting in this from the serial terminal:

    * See attached image, looks like I cant cut text from the serial terminal I'm using.

    Any ideas where that 10^38 is coming from?
    689 x 708 - 113K
  • BradCBradC Posts: 2,601
    edited 2008-12-09 15:41
    Jay Kickliter said...

       dddd := floatM.fdiv(MMmm, 60)
        latitudeDDdd := floatM.fadd(40, dddd))
    
    



    Not remotely an expert, but should those lines be ...
       dddd := floatM.fdiv(MMmm, Float(60))
        latitudeDDdd := floatM.fadd(Float(40), dddd))
    
    



    .. or ..
       dddd := floatM.fdiv(MMmm, 60.0)
        latitudeDDdd := floatM.fadd(40.0, dddd)
    
    

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cardinal Fang! Fetch the comfy chair.
  • Jay KickliterJay Kickliter Posts: 446
    edited 2008-12-09 15:49
    Brad, thanks. float(int) didn't work. But you gave me the idea to try floatm.ffloat(int). Which did work.
  • BradCBradC Posts: 2,601
    edited 2008-12-09 15:53
    Jay Kickliter said...
    Brad, thanks. float(int) didn't work. But you gave me the idea to try floatm.ffloat(int). Which did work.

    That is _really_ strange. All floats are single precision IEEE floats. In theory..
    40.0
    Float(40)
    and floatm.ffloat(40)
    .. should all produce absolutely identical 32 bit constants. If they don't then I think we have a problem.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cardinal Fang! Fetch the comfy chair.
  • Jay KickliterJay Kickliter Posts: 446
    edited 2008-12-09 16:42
    Brad, I'll try again to make sure I didn't make a mistake, and post a reply in the BST thread
Sign In or Register to comment.