How can I check if a string can be converted to a

2020-03-26 20:05发布

问题:

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 the last 15 years so i'm pretty sure this is a total stupid newbie question.

double res; 
errno=0; 
*res = strtod((const char*)literal,NULL); 
if (errno==ERANGE) throw_a_literal_out_of_range_exception();
return res;

but there is no "strtof" function in the c runtime library?

EDIT: To clarify. I already know that the string 'literal' is a valid pattern for a C floating point. It already passed a regular expression test. I just want to check if there is a range/precision problem.

The reason is in Eiffel source code a user can write

a := {REAL_32} 3.1415
b := {REAL_32} 3.0E200

To cast a written floating point number explit to a 32bit floating point. I think in the second case the compiler should detect that the value is out of range and raise an error or at least a warning.

回答1:

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.



回答2:

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 */


回答3:

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



回答4:

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.



回答5:

strtof does not exist in C89, but it does in C99.



回答6:

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.



回答7:

On MSVC you can use _atoflt(), defined in stdlib.h