This code is closely based on the answer by dasblinkenlight. I proffer it as food for thought. Some of the answers it gives may not be what you wanted.
#include <stdio.h>
#include <string.h>
static void test_float(const char *str)
{
int len;
float dummy = 0.0;
if (sscanf(str, "%f %n", &dummy, &len) == 1 && len == (int)strlen(str))
printf("[%s] is valid (%.7g)\n", str, dummy);
else
printf("[%s] is not valid (%.7g)\n", str, dummy);
}
int main(void)
{
test_float("5.23.fkdj"); // Invalid
test_float(" 255. "); // Valid
test_float("255.123456"); // Valid
test_float("255.12E456"); // Valid
test_float(" .255 "); // Valid
test_float(" Inf "); // Valid
test_float(" Infinity "); // Valid
test_float(" Nan "); // Valid
test_float(" 255 "); // Valid
test_float(" 0x1.23P-24 "); // Valid
test_float(" 0x1.23 "); // Valid
test_float(" 0x123 "); // Valid
test_float("abc"); // Invalid
test_float(""); // Invalid
test_float(" "); // Invalid
return 0;
}
Testing on a Mac running macOS Sierra 10.12.6 using GCC 7.1.0 as the compiler, I get the output:
[5.23.fkdj] is not valid (5.23)
[ 255. ] is valid (255)
[255.123456] is valid (255.1235)
[255.12E456] is valid (inf)
[ .255 ] is valid (0.255)
[ Inf ] is valid (inf)
[ Infinity ] is valid (inf)
[ Nan ] is valid (nan)
[ 255 ] is valid (255)
[ 0x1.23P-24 ] is valid (6.775372e-08)
[ 0x1.23 ] is valid (1.136719)
[ 0x123 ] is valid (291)
[abc] is not valid (0)
[] is not valid (0)
[ ] is not valid (0)
The hexadecimal numbers are likely to be particularly problematic. The various forms of infinity and not-a-number could be troublesome too. And the one example with an exponent (255.12E456
) overflows float
and generates an infinity — is that really OK?
Most of the problems raised here are definitional — that is, how do you define what you want to be acceptable. But note that strtod()
would accept all the valid strings (and a few of the invalid ones, but other testing would reveal those problems).
Clearly, the test code could be revised to use an array of a structure containing a string and the desired result, and this could be used to iterate through the test cases shown and any extras that you add.
The cast on the result of strlen()
avoids a compilation warning (error because I compile with -Werror
) — comparison between signed and unsigned integer expressions [-Werror=sign-compare]
. If your strings are long enough that the result from strlen()
overflows a signed int
, you've got other problems pretending they're valid values. OTOH, you might want to experiment with 500 digits after a decimal point — that's valid.
This code notes the comments made to dasblinkenlight's answer: