Question

Using only ANSI C, what is the best way to, with fair certainty, determine if a C style string is either a integer or a real number (i.e float/double)?

Was it helpful?

Solution

Don't use atoi and atof as these functions return 0 on failure. Last time I checked 0 is a valid integer and float, therefore no use for determining type.

use the strto{l,ul,ull,ll,d} functions, as these set errno on failure, and also report where the converted data ended.

strtoul: http://www.opengroup.org/onlinepubs/007908799/xsh/strtoul.html

this example assumes that the string contains a single value to be converted.

#include <errno.h>

char* to_convert = "some string";
char* p = to_convert;
errno = 0;
unsigned long val = strtoul(to_convert, &p, 10);
if (errno != 0)
    // conversion failed (EINVAL, ERANGE)
if (to_convert == p)
    // conversion failed (no characters consumed)
if (*p != 0)
    // conversion failed (trailing data)

Thanks to Jonathan Leffler for pointing out that I forgot to set errno to 0 first.

OTHER TIPS

Using sscanf, you can be certain if the string is a float or int or whatever without having to special case 0, as is the case with atoi and atof solution.

Here's some example code:

int i;
float f;
if(sscanf(str, "%d", &i) != 0) //It's an int.
  ...
if(sscanf(str "%f", &f) != 0)  //It's a float.
  ...

atoi and atof will convert or return a 0 if it can't.

I agree with Patrick_O that the strto{l,ul,ull,ll,d} functions are the best way to go. There are a couple of points to watch though.

  1. Set errno to zero before calling the functions; no function does that for you.
  2. The Open Group page linked to (which I went to before noticing that Patrick had linked to it too) points out that errno may not be set. It is set to ERANGE if the value is out of range; it may be set (but equally, may not be set) to EINVAL if the argument is invalid.

Depending on the job at hand, I'll sometimes arrange to skip over trailing white space from the end of conversion pointer returned, and then complain (reject) if the last character is not the terminating null '\0'. Or I can be sloppy and let garbage appear at the end, or I can accept optional multipliers like 'K', 'M', 'G', 'T' for kilobytes, megabytes, gigabytes, terabytes, ... or any other requirement based on the context.

I suppose you could step through the string and check if there are any . characters in it. That's just the first thing that popped into my head though, so I'm sure there are other (better) ways to be more certain.

Use strtol/strtoll (not atoi) to check integers. Use strtof/strtod (not atof) to check doubles.

atoi and atof convert the initial part of the string, but don't tell you whether or not they used all of the string. strtol/strtod tell you whether there was extra junk after the characters converted.

So in both cases, remember to pass in a non-null TAIL parameter, and check that it points to the end of the string (that is, **TAIL == 0). Also check the return value for underflow and overflow (see the man pages or ANSI standard for details).

Note also that strod/strtol skip initial whitespace, so if you want to treat strings with initial whitespace as ill-formatted, you also need to check the first character.

It really depends on why you are asking in the first place.

If you just want to parse a number and don't know if it is a float or an integer, then just parse a float, it will correctly parse an integer as well.

If you actually want to know the type, maybe for triage, then you should really consider testing the types in the order that you consider the most relevant. Like try to parse an integer and if you can't, then try to parse a float. (The other way around will just produce a little more floats...)

atoi and atof will convert the number even if there are trailing non numerical characters. However, if you use strtol and strtod it will not only skip leading white space and an optional sign, but leave you with a pointer to the first character not in the number. Then you can check that the rest is whitespace.

Well, if you don't feel like using a new function like strtoul, you could just add another strcmp statement to see if the string is 0.

i.e.

if(atof(token) != NULL || strcmp(token, "0") == 0)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top