2

I want to write a function in C that has one string parameter and returns a double number. For example when the string is fsldnf213414fasfa it should return 213414. But it should be also able to return floating points like fasfasf123.412412fasfff as 123.412412.

I've already a function that can extract only integer numbers not floating points:

double get_num(const char* s) 
{
    unsigned int limit = UINT_MAX / 10;
    double value = 0;
    if ( !s ) {
        return 0;
    }
    for ( ; *s; ++s ) {
        if ( value < limit ) {
            if ( isdigit(*s) ) {
                value *= 10;
                value += (*s - '0');
            }
        }
        else {
            return UINT_MAX;
        }
    }
    return value;
}
inix42
  • 51
  • 2
  • 8

5 Answers5

4

First skip over the non-digits, then use strtod to convert the number to a double:

double get_double(const char *str)
{
    /* First skip non-digit characters */
    /* Special case to handle negative numbers and the `+` sign */
    while (*str && !(isdigit(*str) || ((*str == '-' || *str == '+') && isdigit(*(str + 1)))))
        str++;

    /* The parse to a double */
    return strtod(str, NULL);
}

See here for a working example.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

Instead of using isdigit() or otherwise second-guessing strtod(), you can check to see whether it parsed any of the string:

char temp[] = "dgsgsd-3.5454dfds";

char *t1 = temp;
char *t2;
double d;

do
{
    if(*t1 == 0) return INVALID;
    d = strtod(t1, &t2);    
} while(t2 == t1++);

return d;
JasonD
  • 16,464
  • 2
  • 29
  • 44
  • Same question here: But what if the string beginns with a number? Like 12313asfa1231? I assume it drops the following numbers... – inix42 Dec 04 '12 at 09:25
  • Yes, if you expect multiple numbers in the string you would have to check for those. Your function only returns a double. If you need to return more than one number, that's not going to be enough anyway, and you'd need to either return a collection of numbers or to call it multiple times with updated input, until you run out of string. – JasonD Dec 04 '12 at 09:29
  • Okay I understand, you are right. But what if I want it in one double? Like 123132afsaf123123 as 123132123123. I could extract the first numbers but how I could 'add' these to the rest? – inix42 Dec 04 '12 at 09:31
  • strtod() tells you where it finished parsing, so any of the answers given here can be updated to continue parsing additional numbers in the string with minimal effort. Indeed that behaviour is how my answer actually works - t2 will point to the next character after the parsed number,and could be passed back to the caller or looped over. – JasonD Dec 04 '12 at 09:37
  • Ah, you expect a single number to be split? That's different. You could create a duplicate of the string, stripping out invalid characters, and then parse that with strtod(). However I can imagine cases where the answer will be ambiguous - so I'm not sure the problem is well defined. – JasonD Dec 04 '12 at 09:41
  • I was going to ask about your requirement for floating point, but I see you've added a new question about that: http://stackoverflow.com/questions/13701284/char-as-a-decimal-separator-in-c – JasonD Dec 04 '12 at 11:17
  • Good answer. Best to use `strtod()` for a _correct_ parsing. – chux - Reinstate Monica Jan 03 '21 at 23:12
1
for (; *s; s++) {
    if (!isalpha(*s)) {
        value = strtod(s, NULL);
        break;
    }
}

Result:

"fasfasf-123.412412fasfff"
"fasfasf+123.412412fasfff"
"fasfasf.123412412fasfff"

-123.412412
123.412412
0.123412
iabdalkader
  • 17,009
  • 4
  • 47
  • 74
  • How about "dgsgsd%^&£.5454dfds" ? :) – JasonD Dec 04 '12 at 08:42
  • @JasonD if you expect that input, then you have to go with the slower method – iabdalkader Dec 04 '12 at 08:44
  • 2
    The input in the question wasn't well specified, so it's hard to know which answer is best. However I'd question if my approach is much slower - I imagine strtod()'s checking is as efficient as anyone elses, and it's just a matter of function call overhead, assuming the function has not been inlined. Depending in implementation, that may be pretty minimal. Unless performance is necessary and this shows up on a benchmark, I'd not worry about the speed here... – JasonD Dec 04 '12 at 08:49
  • @JasonD yours will always work, I give you that, but a call to `strtod` logically, should do much more work than simple range checking done in `isalpha`, setting `endptr`, error checking, updating `errno` etc...also based on that, `isalpha` and similar functions are more likely to be inlined. However, the overhead is insignificant for small strings, so +1. – iabdalkader Dec 04 '12 at 09:05
  • I think you're almost certainly correct, though it's worth mentioning that isalpha() does observe the locale, so internally it's not necessarily just a simple range check. – JasonD Dec 04 '12 at 09:31
  • This fails various corner cases: `"xyz-x123"` (assumes non-alpha character always begins numeric text), `"INF123"`, (assumes an alpha character does not begin numeric text) – chux - Reinstate Monica Jan 03 '21 at 23:29
0

I hope to help you.

double stringtof(char *s) {
  int i = 0, j, k = 0;
  int llen = 0;

  llen = strlen(s);
  for (i = 0; i < llen; i++) {
    if (isdigit(s[i]) == 1) {
      break;
    }
  }

  char str2[llen - i];

  for (j = i; j < llen; j++) {
    str2[k] = s[j];
    k++;
  }
  // * val=atof(str2);
  return (atof(str2));
}

ideone

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

To extend @JasonD good idea about using strtod().

Complete valid strings include the below and potentially some other esoteric implementation specific ones.

"123.45"
" 123.45"
"+123.45"
"-123.45"
".45"
"INF"  (or maybe INFINTY)
"NAN"
"123,45"  (when , is the locale decimal point
"123e45"
"0x123p45"

Yet it is reasonable to assert that a numeric string is not required to begin with a control character nor space.

Could use is....() functions carefully to reject non-starters like isgraph().

The isgraph function tests for any printing character except space (’ ’).

#include <ctype.h>
#include <stdlib.h>

// Return fail flag.
int seek_double_in_string(double *dest, const char *s) {
  if (s) {
    // Perhaps allow seek_double_in_string(NULL, s) ?
    double dummy;
    if (dest == NULL) {
      dest = &dummy;
    }

    while (*s) {
      if (isgraph(*(unsigned char*)s)) {  // Use s as-if it was `unsigned char *`
        char *endtpr;
        *dest = strtod(s, &endptr);
        if (s != endptr) {
          // Some conversion occurred - pedantic code might want to detect overflow.
          return false; // no error
        }
      }
      s++;
    }
  }

  // Form a default answer if desired like 0.0 or NAN
  *dest = 0;
  return true;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256