views:

4423

answers:

10

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)?

+2  A: 

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.

nutbar
And the ',' character. Don't forget about localisation.
Arafangion
+4  A: 

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

itsmatt
This doesn't work for 0 though...
Patrick
Sure - your answer certainly covers that case.
itsmatt
I've changed my display name to stop this confusion!
Patrick_O
A: 

Are you assuming that it definately is either an int or a double/float? Or could it be string of characters?

lewis
It is definitely either an integer or real.
Christopher Dolan
+1  A: 

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...)

Vincent Robert
+17  A: 

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.

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.

Patrick_O
sorry, I knew that this is quite old, but in this line, we do not have any errno? unsigned long val = strtoul(to_convert, Is the correct would beunsigned long errno = strtoul(to_convert,
vodkhang
@vodkhang This is valid code, but you may have to use #include <errno.h> depending on your compiler. errno is essentially a global variable that can be set with an error code. It may be implemented as a macro or a "modifiable lvalue".
Ben Gartner
+1  A: 

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.

+4  A: 

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.
  ...
Patrick
I'm pretty sure you need to test the return value of sscanf against the sizeof str to make sure the whole string was converted, otherwise the "%d" won't fail if it's handed "1.374", it will return 1.
Patrick_O
True. This could also be solved by testing for %f first, but that causes issues with "1.". Personally, I like your solution the best of those presented.
Patrick
A: 

I may be way off here, but couldn't you just use modulo?

a = 1.8; 
if (a % 2 == 0) {  } //is int
else { } //is float

Assuming of course that the input MUST be either an int or a float.

lewis
This is not actually correct C code.... You can't use % on a float. Also, a % 2 == 0 tests for even integers only.
Colin
-1 This is not testing the type of a string
Tomas
+2  A: 

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.

Steve Jessop
+3  A: 

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.

Jonathan Leffler
I have added an errno = 0; to my code example, thank you.
Patrick_O