Shop OBEX P1 Docs P2 Docs Learn Events
Need a little help taking a string from the PC and converting it to a floating — Parallax Forums

Need a little help taking a string from the PC and converting it to a floating

grasshoppergrasshopper Posts: 438
edited 2008-06-21 23:23 in Propeller 1
I am still working on this code and MIKE GREEN Big thanks.

Now i need to receive a string that contains a number from the PC. something like this Min number (F1 ZZ S -20.32) up to (F1 ZZ S 150.00)

Here is my code that i am using and i may be going about it wrong.


CON
  _clkmode      = xtal1 + pll16x
  _xinfreq      = 5_000_000

  SPACE         = $20

obj
SER           : "FullDuplexSerialExtended"

VAR

long    Trash  
long    RxString [noparse][[/noparse] 18 ]
long    String_Counter 


PUB START

  SER.start(31, 30, 0, 9600)                        'Turn on serial com object


repeat
  
    STRING_COUNTER := 0  
                                    
    Repeat until ( RxString[noparse][[/noparse]STRING_COUNTER] := Ser.RX) == ")"
        STRING_COUNTER ++
  
    IF (RxString [noparse][[/noparse] 0 ] == "(") & (RxString [noparse][[/noparse] 1 ] == "F") & (RxString [noparse][[/noparse] 2 ] == "1") & (RxString [noparse][[/noparse] 3 ] == SPACE) & (RxString [noparse][[/noparse] 6 ] == SPACE) ' CHECK FOR THE CONDITION "(F1 "

                  IF (RxString [noparse][[/noparse] 4 ] == "Z") & (RxString [noparse][[/noparse] 5 ] == "Z") & (RxString [noparse][[/noparse] 7 ] == "S")& (RxString [noparse][[/noparse] 8 ] == SPACE)                            ' CHECK FOR THE CONDITION "ZZ S "
                
                                IF (RxString [noparse][[/noparse] 9 ] == "-") & (RxString [noparse][[/noparse] 12 ] == ".")  ' CHECK FOR THE DECIMAL
                                
                                                        Trash :=  (RxString [noparse][[/noparse] 10 ] + RxString [noparse][[/noparse] 11 ] + RxString [noparse][[/noparse] 13 ] + RxString [noparse][[/noparse] 14 ] )
                                                         
                                                         ser.dec(trash)   'TRANSMIT THE NUMBER TO TEST IT 
                                                           
                                IF ( RxString [noparse][[/noparse] 9 ] == "1") & ( RxString [noparse][[/noparse] 12 ] == ".")    'CHECK FOR 0NE HUNDRED                 
                                                        Trash := (RxString [noparse][[/noparse] 9 ] + RxString [noparse][[/noparse] 10 ] +RxString [noparse][[/noparse] 11 ] + RxString [noparse][[/noparse] 13 ] + RxString [noparse][[/noparse] 14 ]) 
                                                         
                                                         ser.dec(trash)  ' TRANSMIT THE NUMBER FOR 100.00
                                                         
                                IF ( RxString [noparse][[/noparse] 11] == ".") & (String_Counter == 14)  ' CHECK THE END OF THE STRING COUNTER
                                                        Trash :=  (RxString [noparse][[/noparse] 9 ] + RxString [noparse][[/noparse] 10 ] + RxString [noparse][[/noparse] 12 ] + RxString [noparse][[/noparse] 13 ])
                                                        
                                                         ser.dec(trash) ' TRANSMITT THE NUMBER FOR 20.12
                                                         





I need a different approach i think

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 00:11
    The best way to go about this is to scan the number (000.000) converting it to integer on the fly
    value := (value * 10) + (character - "0")
    If you encounter a decimal point, you then keep track of the number of decimal places, but
    continue to accumulate the integer value. Obviously, you're limited to about 10 digits unless
    you make special provisions for more, but 10 digits is plenty. Once you find a non-digit or a
    second decimal point, you use the number of decimal places to look up a floating point power
    of ten in a table. Ten entries is more than enough. You then convert the integer to floating
    point and divide by the proper power of ten to get your result.

    I don't have the time now to write a sample routine, but I'll try to post something this weekend.
  • grasshoppergrasshopper Posts: 438
    edited 2008-06-20 00:34
    Mike i am more confused now.

    My number system will range from ( -50.00 to 150.00 ) that is all. No more than 2 decimal places at any time. Can you help me understand more on what you mentioned above?

    Jazzed: Thanks for the example, it will take me some time to figure out what you are trying to do in the code.
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 00:45
    grasshopper,

    This is one of those cases where a general purpose routine is probably simpler, certainly smaller, and perhaps just as fast as a more limited special case routine.

    Try doing what I suggested by hand on paper and see what you get.
  • Jimmy W.Jimmy W. Posts: 112
    edited 2008-06-20 01:03
    Mike Green said...
    The best way to go about this is to scan the number (000.000) converting it to integer on the fly
    value := (value * 10) + (character - "0")
    If you encounter a decimal point, you then keep track of the number of decimal places, but
    continue to accumulate the integer value. Obviously, you're limited to about 10 digits unless
    you make special provisions for more, but 10 digits is plenty. Once you find a non-digit or a
    second decimal point, you use the number of decimal places to look up a floating point power
    of ten in a table. Ten entries is more than enough. You then convert the integer to floating
    point and divide by the proper power of ten to get your result.

    I don't have the time now to write a sample routine, but I'll try to post something this weekend.

    just so that grasshopper understands better:
    decp := 0
    points := 0
    value := 0
    WHILE char := serial.rxcheck
      case char
        46:       decp = 1                                           'its a decimal point
        48..57: value := (value * 10) + (char - "0")     'its a number, process the digit
                    if decp                                              ' we previously found a decimal point, increment how many digits we found behind the decimal point
                     points++ 
    
    'when its all done you have an integer from a float, 12.97 would be 1297 and points would be 2
    
    
    
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 04:19
    Well, here's a first try:
    OBJ Flt : "FloatMath" ' Change to same package used in other objects
    
    DAT
    table  long  10.0, 100.0, 1_000.0, 10_000.0, 100_000.0
           long  1_000_000.0, 10_000_000.0, 100_000_000.0
           long  1_000_000_000.0
    
    PUB StrToFloat(strPtr, fltPtr) | value, decPlace, char, sign
    ' This routine takes a pointer to a character string (strPtr) and
    ' a pointer to a long where the floating point result is to be put.
    ' It returns an updated string pointer (to the delimiter of the
    ' number).  Values must consist of an optional plus or minus sign
    ' followed by a series of digits with an optional decimal point.
    ' The string is terminated by a 2nd decimal point or any other
    ' non-digit character.  No error checking is done.  In particular,
    ' no checking for overflow is done.  More than 9 significant digits
    ' may result in an incorrect result.
       value~                         ' Initialize variables
       decPlace~~
       case sign := byte[noparse][[/noparse]strPtr]      ' Check for sign
          "+", "-":
             strPtr++
       repeat
          case char := byte[noparse][[/noparse]strPtr++] ' Check next character
             "0".."9":
                value := (value * 10) + (char - "0")
                if decPlace <> -1     ' Keep track of # decimal places
                   decPlace++
             ".":
                if decPlace <> -1     ' Only one decimal point allowed
                   quit
                decPlace~
             other:                   ' Some non-digit was found
                quit
       value := Flt.Float(value)      ' Convert to floating point
       if sign == "-"
          value := Flt.FNeg(value)    ' Account for negative sign
       if decPlace <> -1
          if decPlace > 0
             value := Flt.FDiv(value,table[noparse][[/noparse]decPlace-1]) ' Adjust for # decimals
       long[noparse][[/noparse]fltPtr] := value          ' Store result
       return strPtr - 1              ' Return address of terminator
    

    Post Edited (Mike Green) : 6/20/2008 3:55:42 PM GMT
  • grasshoppergrasshopper Posts: 438
    edited 2008-06-20 15:30
    Wow Thanks fellas I just realized that I need to learn so much more in this programming language. I am stuck on old habits and cant figure some of thie new stuff out just yet.

    When you do things like this what is it for

    1.
    PUB StrToFloat(strPtr, fltPtr) | value, decPlace, char, sign
    
    



    1. Whats the (strPtr , filPtr) mean? the rest of this line I understand

    2.
       value~                         ' Initialize variables
       decPlace~~
    
    




    2. Is this just a fancy way to make the 2 variables 0 ? or inputs?


    Jeez ill have to look at this code for weeks to fully understand it. I mean how can i implement it in my code?
    But thanks so far for the help i do appreciate it.

    Post Edited (grasshopper) : 6/20/2008 3:36:03 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 15:44
    grasshopper,
    Please read the comment after the PUB. It explains what strPtr and fltPtr are.
    Read the Propeller manual in the section on PUB for a description of the syntax of a method declaration.
    The stuff in parentheses is a list of parameters to the method. If you have a byte array "str" containing
    the string and a long "fltValue" where the floating point value is to go, you'd call s := StrToFloat(@str,@fltValue)
    where "s" is a long to hold the address of the terminating character.

    The "~" and "~~" are simple ways to set the variables to zero or all-one-bits. Look in the Propeller manual
    whenever you see something new. It's all documented with very few exceptions.
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 15:55
    There was an error in the use of the # decimal places. Sorry.
    The posted code has been corrected to divide by the proper power of 10 if 1 or greater.
  • grasshoppergrasshopper Posts: 438
    edited 2008-06-20 16:40
    Ok it is making since now but I have a problem and maybe I am going about it incorrectly.

    my new up dated code below
    
    Pub getcom 
      
      StringCount := 0
      repeat until (RxDat [noparse][[/noparse] StringCount ] := SER.rx ) == ")"
        StringCount ++
    
      IF (rxDat [noparse][[/noparse] 0 ] == "(")&(rxDat [noparse][[/noparse] 1 ] == "F")&(rxDat [noparse][[/noparse] 2 ] =="1")* (rxDat [noparse][[/noparse] 3 ] == SPACE)
    
                                     
              IF (rxDat [noparse][[/noparse] 4 ] == "Z")&(rxDat [noparse][[/noparse] 5 ] == "Z")&(rxDat [noparse][[/noparse] 6 ] ==SPACE)&(rxDat [noparse][[/noparse] 7 ] == "S")* (rxDat [noparse][[/noparse] 8 ] == SPACE)    
                                   SER.STR(STRING("(F1 ZZ S "))
    
                                   TEMP := RXDAT [noparse][[/noparse] 9 ] + RXDAT [noparse][[/noparse] 10 ]+RXDAT [noparse][[/noparse] 11 ]+RXDAT [noparse][[/noparse] 12 ]+RXDAT [noparse][[/noparse] 13 ]+rxdat [noparse][[/noparse] 14 ]+rxdat [noparse][[/noparse] 15 ] ' add up the value and store it in TEMP
    
                                   STRTOFLOAT(TEMP,Value) ' send it all to mikes method ? ? 
    
                                   SER.STR(fs.Floattostring(Value))
      
                                   SER.STR(STRING(")"))  
    
    



    Am i addressing the method correctly ? "STRTOFLOAT(TEMP,DECNUMBER)" I made your local "value" variable a global variable is this ok or did i mess it up.
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-20 17:22
    Sorry to say that you seem to not understand some basic ideas.

    Do you understand pointers? These are values that are the address of things.
    If you have a byte array like rxDat, this can contain a string, one character per byte.
    The address of the byte array is represented as @rxDat.

    In general, don't take a local variable and make it a global one unless you understand
    exactly what the method is doing with the local variable. Don't modify a library routine
    or some example that someone has posted until you understand how it works unchanged.

    In the case that you posted, use StrToFloat(@rxDat + 9, @Value)
    This is not the same variable used in StrToFloat.
    The routine starts scanning at the tenth character position (start + 9).

    Post Edited (Mike Green) : 6/20/2008 5:28:23 PM GMT
  • grasshoppergrasshopper Posts: 438
    edited 2008-06-21 17:14
    Mike:
    Thanks so much. I did some more reading in the Prop book and learned what you are speaking about to some extent. My only problem is that you have no return vale that i can see. In this line of code
    PUB StrToFloat(strPtr, fltPtr) | value, decPlace, char, sign
    
    



    Should it not be
    PUB StrToFloat(strPtr): fltPtr | value, decPlace, char, sign
    
    



    if so I would then address it as

    temp := StrToFloat(@rxDat +9)
    
    



    Please shed some light on this for me
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-21 18:05
    The reason for returning the modified strPtr value is that it contains the address of the character that caused the scan to stop.
    You can look at that character for error checking, scan past it, and call StrToFloat with a new pointer pointing to the next
    number in the input stream. You can only return one value from a function, so passing the address of a variable gives a way
    for returning additional values from the function. If there's no valid number at the initial string address, the floating point
    value will be zero. I assume that you don't call StrToFloat unless you know there's a valid number in the string. You can
    check for that by comparing the returned string address with the one you furnish. If they're the same, there was no number
    at that address.

    If it's more convenient, you can change this all by having the function return the floating point value and passing a single
    parameter which is the address of a long containing the address of the string pointer. This string pointer would be updated
    by the function to point to the terminating character. This actually might make scanning multiple values easier.

    Post Edited (Mike Green) : 6/21/2008 6:11:38 PM GMT
  • Mike GreenMike Green Posts: 23,101
    edited 2008-06-21 18:16
    Here's the modified routine:
    OBJ Flt : "FloatMath" ' Change to same package used in other objects
    
    DAT
    table  long  10.0, 100.0, 1_000.0, 10_000.0, 100_000.0
           long  1_000_000.0, 10_000_000.0, 100_000_000.0
           long  1_000_000_000.0
    
    PUB StrToFloat(strPtrPtr) : value | strPtr, decPlace, char, sign
    ' This routine takes a pointer to a long which contains a pointer
    ' to a character string (strPtrPtr).  It updates this long to contain
    ' the address of the character containing the terminating character.
    ' It returns the resulting floating point value.  The string must
    ' consist of an optional plus or minus sign followed by a series of
    ' digits with an optional decimal point.  The string is terminated
    ' by a 2nd decimal point or any other non-digit character.  No
    ' error checking is done.  In particular, no checking for overflow
    ' is done.  More than 9 significant digits may result in an
    ' incorrect result.  If the string contains no valid number,
    ' the result will be a floating point zero and the string pointer
    ' will be unchanged.
       decPlace~~
       strPtr := long[noparse][[/noparse]strPtrPtr]
       case sign := byte[noparse][[/noparse]strPtr]      ' Check for sign
          "+", "-":
             strPtr++
       repeat
          case char := byte[noparse][[/noparse]strPtr++] ' Check next character
             "0".."9":
                value := (value * 10) + (char - "0")
                if decPlace <> -1     ' Keep track of # decimal places
                   decPlace++
             ".":
                if decPlace <> -1     ' Only one decimal point allowed
                   quit
                decPlace~
             other:                   ' Some non-digit was found
                quit
       value := Flt.Float(value)      ' Convert to floating point
       if sign == "-"
          value := Flt.FNeg(value)    ' Account for negative sign
       if decPlace <> -1
          if decPlace > 0
             value := Flt.FDiv(value,table[noparse][[/noparse]decPlace-1]) ' Adjust for # decimals
       long[noparse][[/noparse]strPtrPtr] := strPtr   ' Update string pointer
    

    Post Edited (Mike Green) : 6/22/2008 12:15:03 AM GMT
  • grasshoppergrasshopper Posts: 438
    edited 2008-06-21 23:23
    I have learned so much more from this example. I am sure will bug some of you more in the future. THANKS A MILLION/
Sign In or Register to comment.