Thread: Convert string to float without using library functions?

  1. #1
    Registered User
    Join Date
    Dec 2009
    Posts
    27

    Convert string to float without using library functions?

    First of all, this is not homework; I'm "fifty-eleven"

    I'm working on a microcontroller project and I need to convert a string (from a NMEA GPS receiver) into a float, I have very limited memory left, and loading an additional library uses it all up.

    The strings have varied format, with up to 5 digits before the DP (including leading zeros), and up to 3 digits after (including trailing zeros).

    NOTE: the number will later be multiplied by 100 to remove the need for further float processing (I will round off the 3rd digit after the DP, if any).

    I found this code that looks neat, although I don't think it can handle leading zeros [Edit: I may be wrong? It's after midnight!]:

    Code:
    float result = 0.0 ;
    float scale  = 0.0 ;
    
    char*s = "00103.170" ;  // example, need to return float 103.17 
    
    while(*s) // do while not \0 string terminator
    { 
      if((*s >= '0') && (*s <= '9')) // numeric character 
      {
        result = 10.0 * result + (float)(*s - '0') ; // shift existing result to next column, add current character cast as float
        scale *= 10.0 ;      // this only does something when scale not 0.0, so after DP found
      }
    
      elseif(*s == '.') // not numeric, so if DP ...
        scale = 1.0 ;  // ... start building divisor for use at exit
    
      else // not numeric or DP so error
        {/* DO ERROR HANDLING */}
    
      s++ ; // next character 
    
    } // exit at end of string \0
    
    result = result / (scale == 0.0 ? 1.0 : scale) // divide by 1.0 if scale is 0.0, or by scale if scale not 0.0 due to DP found
    Source: post by Ingo August 17'11 at 13.57 with changes in formatting and my comments

    Any comments or suggestions?
    Last edited by nigelmercier; 01-01-2016 at 06:18 PM.

  2. #2
    Registered User
    Join Date
    Sep 2014
    Posts
    364
    Quote Originally Posted by nigelmercier View Post
    The strings have varied format, with up to 5 digits before the DP (including leading zeros), and up to 3 digits after (including trailing zeros).

    NOTE: the number will later be multiplied by 100 to remove the need for further float processing (I will round off the 3rd digit after the DP, if any).
    I asume that you allways multiply by 100, so you can scan the number directly to integer.
    This function also round the last digit according to the 3rd fractal position and pay attention to the sign.
    Code:
    #include <stdio.h>
    
    int NMEA_int (char *str) {
    
        int digit = 0, frac = 0, sign = 1;
    
        while (1) {
            if (digit == 0 && frac == 0 && *str == '-') sign = -sign;
            else if (*str == '.') frac++;
            else if (*str >= '0' && *str <= '9') {
                if (frac < 3) {
                    digit = digit * 10 + (*str - '0');
                }
                else {
                    if (*str - '0' > 4) digit++;
                }
                if (frac) frac++;
            }
            else break;
            if (frac > 3) break;
            str++;
        }
    
        if (frac == 0) frac = 1;
        for (; frac < 3 ; frac++) digit *= 10;
    
        digit *= sign;
        return digit;
    }
    
    
    int main (void) {
    
        int a;
        char *s = "00103.170";
        
        a = NMEA_int(s);
        printf ("int = %d\n", a);
        return 0;
    }
    Last edited by WoodSTokk; 01-01-2016 at 06:32 PM.
    Other have classes, we are class

  3. #3
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Personally, I'd use a small state machine and a "consumer" approach, so you can avoid having to have a buffer for the GPS response string altogether. If you can show what one complete GPS response looks like, I could show some actual example code.

    The idea is that you have one global variable, for example
    static unsigned char gps_state = 0;
    that tracks which logical part of the GPS response is being processed, and a function
    unsigned char gps_char(const unsigned char in);
    that you call for each incoming GPS response character, instead of storing it in a buffer. (You can, of course, just call it for each character in a buffer, if you have a buffer already.)

    If the function returns zero, it just means the GPS response is incomplete. When the function returns nonzero, you have a complete set of numbers, for example already multiplied by 1000 (as 999,999,999 fits in an 32-bit integer, both signed and unsigned) in global variables, say
    static int gps_longitude, gps_latitude;
    Next call to gps_char() will reset the state, and reset those global variables (so you might need to copy them elsewhere for use).

    It might sound complicated, but it is actually quite simple and compact code. Mostly, it's just that the viewpoint -- doing it via a function that consumes the GPS response one character at a time, and just occasionally tells us it has a full set of coordinates available -- is uncommon.

  4. #4
    Registered User
    Join Date
    Dec 2009
    Posts
    27
    Thanks for the replies. A few points:

    The "number" is always positive.

    I've already written the code to parse the NMEA string. It does it on the fly, extracting each field into a short string buffer; most of these are discarded, the required fields are copied to other strings during the processing. Once fully tested I'll post it on this forum so you can pick it apart

    My main reason for posting the code I found was to check its validity for the task, and seek any improvements. It is quite hard to debug code when you only have a single LED for output; I'm still learning how to use the debugger.

  5. #5
    Registered User Sam Jackson's Avatar
    Join Date
    Jan 2015
    Posts
    5
    if you already have stdio loaded you can use sscanf:

    char
    *s = "00103.170" ;
    float fNum;
    sscanf(s, "%f", &fNum);

  6. #6
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    The stated challenge was "without using library functions", and sscanf is a "library function".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  7. #7
    Registered User Sam Jackson's Avatar
    Join Date
    Jan 2015
    Posts
    5
    Quote Originally Posted by laserlight View Post
    The stated challenge was "without using library functions", and sscanf is a "library function".
    The stated challenge was without loading an ADDITIONAL library.

  8. #8
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Ah, that is true.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  9. #9
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by Sam Jackson View Post
    if you already have stdio loaded you can use sscanf:

    char
    *s = "00103.170" ;
    float fNum;
    sscanf(s, "%f", &fNum);
    Quote Originally Posted by Sam Jackson View Post
    The stated challenge was without loading an ADDITIONAL library.
    But your proposed solution implies that stdio has already been included, which was not explicitly confirmed by the OP. While this may be a fair assumption for computer programs, it is not as fair for microcontroller code. Therefore, in the context of a microcontroller, stdio.h might very be considered an "additional library".

    I myself have had to create custom "string to float"/"float to string" functions for certain microcontrollers because the overhead of using "standard" library functions ("sscanf()", "sprintf()", etc) in those environments was too great for the limited memory of those devices. While we don't know if this same memory constraint that is limiting the OP, it would be a fair assumption based on the original question.

  10. #10
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Matticus
    But your proposed solution implies that stdio has already been included, which was not explicitly confirmed by the OP. While this may be a fair assumption for computer programs, it is not as fair for microcontroller code. Therefore, in the context of a microcontroller, stdio.h might very be considered an "additional library".
    Yeah, but Sam Jackson carefully prefaced the suggestion with "if you already have stdio loaded", and with that qualification the suggestion is sound.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  11. #11
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    The stated challenge was without loading an ADDITIONAL library.
    But is the floating point software library already loaded as well? The software floating point library can be fairly large and slow on some microprocessors so unless it is actually required using it is often better avoided.

    Jim

  12. #12
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by laserlight View Post
    Yeah, but Sam Jackson carefully prefaced the suggestion with "if you already have stdio loaded", and with that qualification the suggestion is sound.
    But even if stdio is already included, chances are that calling such a function will add unnecessary overhead. Since the OP already specified that the remaining memory was very limited, I was explaining how the proposed solution might not meet their criteria.

    I agree that it is a suggestion the OP should consider if they haven't already, but they should also be aware that it might not be able to solve their problem given the constraints.

  13. #13
    Registered User
    Join Date
    Dec 2009
    Posts
    27
    I've used the code I suggested, it seems to work. I have 27 bytes of ROM left!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 06-22-2010, 05:13 AM
  2. Convert string to float
    By ching13 in forum C Programming
    Replies: 6
    Last Post: 05-07-2010, 02:58 PM
  3. Convert string to float?
    By tesla in forum C++ Programming
    Replies: 7
    Last Post: 09-22-2009, 05:39 AM
  4. help in convert String to float
    By kahwai1984 in forum C++ Programming
    Replies: 7
    Last Post: 03-19-2006, 09:21 PM
  5. convert float to string
    By vthokienj in forum C++ Programming
    Replies: 3
    Last Post: 12-14-2002, 11:33 AM