First my context is that of a compiler writer who needs to convert floating point literals (strings) into float/double values. I haven\'t done any floating point programming
Since you have your value in a double
, you can just check if it's outside of the range of float
:
#include <stdlib.h>
#include <stdio.h>
#include <float.h>
int main(int argc, char *argv[])
{
double d = FLT_MAX;
if (argc > 1) {
d = strtod(argv[1], NULL);
}
if ((d > 0 && (d > FLT_MAX || d < FLT_MIN))
|| (d < 0 && (d < -FLT_MAX || d > -FLT_MIN)))
printf("Invalid float: %g\n", d);
else
printf("Valid: %g\n", d);
return EXIT_SUCCESS;
}
Running the program:
$ ./a.out
Valid: 3.40282e+38
$ ./a.out 3.5e38
Invalid float: 3.5e+38
$ ./a.out -1e40
Invalid float: -1e+40
You may or may not need to add a test for correct strtod()
return, depending upon whether there's a possibility of an overflow in double
type as well.
In C89 you may use sscanf.
For example:
float myfloat;
if(sscanf(str, "%f", &myfloat) != 1)
/* String could not be converted */
else
/* String was converted, myfloat is usable */
@Nicholas Goy:
I don't think sscanf(str, "%f, &float) == 1
(or != 1
) really does what's desired.
If there are additional characters in str
(e.g. "1.1foo"), it will parse without error, which is probably undesirable. This can be rectified by doing:
char dummy;
if (sscanf(str, "%f%c", &float, &dummy) != 1) {
/* Parse error. */
} else {
/* Parsed cleanly. */
}
instead. However, sscanf
with %f
is likely to use strtod
internally and cast to float anyway. The language in the C standard says:
a,e,f,g Matches an optionally signed floating-point number, infinity, or NaN, whose format is the same as expected for the subject sequence of the strtod function. The corresponding argument shall be a pointer to floating.
which sort of implies this, and it seems to be true for me (gcc 4.2.1 on FreeBSD). Using the above sscanf
code, "1.79e308" parses successfully but has the value +inf, as does "5e-300" with the value 0.0, and these are the same results you'd get from (float) 1.79e308
and (float) 5e-300
.
Anyway. All that said, I question why the OP wants to use float instead of double anyway.
I was going to say, simply use the code you already have, but assign the result of strod() to a float instead of a double. But your code is wrong in several ways.
Firstly, you cannot test errno unless an error has ocurred. Secondly, strtod() will not set errno except for things like range errors. If you pass it an invalid number, like "XYZ", it will not be set.
More correct use of strtod is:
char *p;
double d = strtod( "123.4xyz", & p );
if ( * p != 0 ) {
// not a number - in this case will point at 'x'
}
Using strtod() to read a float, you may lose some precision, but that's the price you pay for using floats - in general, unless you have a very good reason not to, you should always prefer the use of double to float.
I suggest converting to double first, then cast to float. If the relative difference, (f-d)/f
, is greater than float precision (roughly, 1e-7
) then there are more digits than what can be safely stored in float.
strtof
does not exist in C89, but it does in C99.