0

I saw this answer (you can use the minor example in Ideone there, it's equivalent), so I wrote the function below to do the job, but it will stop when reading the dot before the decimal digits and will treat pi like 3 and 14. How to change the code to extract the real number? I do not care about speed or memory usage, I just want to do the job.

float extract_number(char* str) {
    char* p = str;
    while (*p) {
        if (isdigit(*p)) {
            long val = strtol(p, &p, 10);
            printf("%ld\n", val);
            return val;
        } else {
            p++;
        }
    }
    return -1.0;
}

Here is the str I am working with:

Initializing data structure(s) took 149.898690000 seconds wall clock time.
Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305

2 Answers2

2

Personally, I like sscanf for string parsing:

double extract_number(char* str) {
  double x;
  int result = sscanf (str, "%lf", &x);
  if (result != 1)
    return -1.0;
  return x;
}

NOTE:

If you want to separate the integral and fractional parts of a floating point value, look at modf().

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • -1 Input overflow triggers undefined behavior, therefore it is unsafe to use the `scanf` functions to parse numbers, period. – zwol Jul 12 '15 at 20:50
  • Paul, really sorry, but my question was not clear. Updated and your code won't work now. – gsamaras Jul 12 '15 at 20:50
  • @gsamaras: 1) sscanf() is perfectly appropriate: you can check for "illegal string" by verifying "result == 1" (successfully read the "%lf" argument). 2) Use "modf()" to split integer/fraction (if your platform supports it). 3) Using "-1" to flag "illegal input" probably isn't the most robust solution. I would consider adding an "error flag" either as your function return, or as a reference parameter. In any case, I much prefer "sscanf()" to "strtod()". Because you don't have to mess with errno - which everybody and his brother uses, and which isn't always thread-safe). IMHO... – paulsm4 Jul 12 '15 at 21:09
  • @paulsm4 I'm sorry, it does not work to "check for 'illegal string' by verifying 'result == 1'", because, again, *input overflow triggers **undefined behavior**.* See the longer discussion on this answer: http://stackoverflow.com/questions/24302160/scanf-on-an-istream-object/24318630#24318630 The *only* fully reliable (that is, all errors are detectable and the behavior is fully defined regardless of input) numeric conversion functions in the C library are the `strto*` functions. – zwol Jul 13 '15 at 14:18
  • @paulsm4 Also, FYI, `errno` is required to be thread-safe by both POSIX.1(-2001) and C11 (which is the first C standard revision to have any concept of threads) and I haven't tripped over an implementation that got that wrong in at least 15 years. – zwol Jul 13 '15 at 14:22
2

strtol converts text to a long, not a float. From strtol:

Interprets an integer value in a byte string pointed to by str.

Use either strtof or strtod to convert to a float or double. The prototype uses a second argument, commonly called endptr, which is a char * that will be updated to point to the first not-converted character, but since you don't need it here you can set it to NULL.

A couple more optimizations on that routine:

  1. you can safely modify the argument *str
  2. no need for an intermediate value, you can return the result of strtof
  3. no need for an else after an explicit return
  4. if the value -1.0f is a possible valid return value, you should use NAN.
float extract_number (char* str)
{
    while (*str)
    {
        if (isdigit(*str))
        {
            return strtof(str, NULL);
        }
        str++;
    }
    return NAN;
}

To return NAN you need to include math.h, but it is a fairly recent addition (and, apparently, an omission before!) so it may not be available in your system. If it isn't, use this totally wicked hack (kudos to J.Kraftcheck's answer here):

return strtof("NaN", NULL);
Community
  • 1
  • 1
Jongware
  • 22,200
  • 8
  • 54
  • 100