views:

415

answers:

7

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.

+4  A: 

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

Thomas
Okay i forgot to mention the compiler: MSVC 2005.
Lothar
+8  A: 

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 */
nice avatar :) [15 chars]
Andreas Bonini
+3  A: 

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.

Pavel Radzivilovsky
+2  A: 

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

Hans Passant
+3  A: 

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.

anon
Yes of course - I seem to be having a few problems this afternoon. Thanks.
anon
Well the lexer already gives me a lexical valid number sequence. The only thing i have to do is looking for range errors. Thats why i have written that i'm builing a compiler. I think it should give a warning when the literal value is wrong and the compiler can detect this.
Lothar
In that case I'd take the easy way out and not support the float type, only doubles. floats aren't particularly useful.
anon
Well unfortunately there is an Eiffel language standard i have to implement. And too many 3rd party libraries use floats in there API
Lothar
+4  A: 

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

jamesdlin
Because i'm writing an Eiffel compiler (well it's a huge Eiffel->C preprocessor) and user can write "{REAL_32}3.1415" to get a 32bit real number instead of default 64bit. I think the compiler should throw an error or warning if the source code already contains a precision loss. And by the way, i already know that the string is a pure syntax valid floating point number string, i only care about range and precision errors. Sorry - i missed this detail in the question.
Lothar
Well, I don't know how strict the OP wanted the conversion, but yea, my scanf code will ignore trailing char (as strtod).
@Nicholas Goy: strtod won't return an error on the trailing char, but you at least can use the end pointer to verify that it's pointing to the expected end of the string. The scanf code provides no advantage over strtod.
jamesdlin
+1  A: 

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.

Alok