Problems With strtof()

J

Jonathan Lamothe

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've written a program that requires the user to input a decimal number
which is then stored as a float.

First it's read into a variable buf which is an array of 16 characters
like this:
fgets(buf, 16, stdin);

I then tried to convert it into a float and store it in another variable.
val = strtof(buf, (char **)NULL);

For some reason, all numbers other than 0 are not being converted
properly. Am I doing something wrong?

I've changed the line of code to read like this:
val = (float)strtod(buf, (char **)NULL);

The second line of code is working fine, but it still bothers me that
the first one isn't.

If anyone wants to see the source a tarball is available at
ftp://linserv.homeip.net/src/tempconv
- --
Regards,
Jonathan Lamothe

QOTD:

"Backups are for wimps. Real men upload their data to an FTP site and
have everyone else mirror it."

-- Linus Torvalds
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFCRxRBNrv4JaRC3JsRAsqwAJ483+dEEJYbR7nerGoGDwyBO4W2pACeJbrQ
kM69g/hPe60nF34XbYgnOoM=
=g0AN
-----END PGP SIGNATURE-----
 
J

Joe Wright

Jonathan said:
I've written a program that requires the user to input a decimal number
which is then stored as a float.

First it's read into a variable buf which is an array of 16 characters
like this:
fgets(buf, 16, stdin);

I then tried to convert it into a float and store it in another variable.
val = strtof(buf, (char **)NULL);

For some reason, all numbers other than 0 are not being converted
properly. Am I doing something wrong?

I've changed the line of code to read like this:
val = (float)strtod(buf, (char **)NULL);

The second line of code is working fine, but it still bothers me that
the first one isn't.

The first line doesn't work because strtof() doesn't exist. Typo?

float val;
val = strtod(buf, NULL);

... will work just fine. strtod() doesn't need a cast and the second
argument is never cast. It would be used like this ..

float val;
char *endp;
val = strtod(buf, &endp);
 
B

Barry Schwarz

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've written a program that requires the user to input a decimal number
which is then stored as a float.

First it's read into a variable buf which is an array of 16 characters
like this:
fgets(buf, 16, stdin);

This looks reasonable.
I then tried to convert it into a float and store it in another variable.
val = strtof(buf, (char **)NULL);

The cast is unnecessary but also not harmful.

Unfortunately, there is no strtof in standard C before C99. Do you
really have a C99 compiler or is this a typo?
For some reason, all numbers other than 0 are not being converted
properly. Am I doing something wrong?

How do you know? What are you using to examine the result?
I've changed the line of code to read like this:
val = (float)strtod(buf, (char **)NULL);

Other than possibly silencing an obnoxious warning, the first cast is
irrelevant but again not harmful.

Why don't you create a char* and pass it's address so you can see
where the function is having a problem.
The second line of code is working fine, but it still bothers me that
the first one isn't.

If anyone wants to see the source a tarball is available at
ftp://linserv.homeip.net/src/tempconv

You have gone ballistic with casts. A cursory review shows that all
but two are absolutely unnecessary and I think the remaining two are
also unnecessary.

Of course, the fact that your code will not compile cleanly (undefined
identifier VERSION) doesn't lend any confidence that this is the
actual code you are talking about.

Your entire program is only about 170 lines. Why didn't you paste it
in so we could see what you were talking about?


<<Remove the del for email>>
 
J

Jonathan Lamothe

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Barry said:
The cast is unnecessary but also not harmful.

Unfortunately, there is no strtof in standard C before C99. Do you
really have a C99 compiler or is this a typo?

Well, it's not a typo and it compiles, so I'm going to go out on a limb
and say that I have a C99 compiler. It's whatever came with my copy of
Linux.

Thanks for pointing that out, though. I'll keep it in mind from now on.
How do you know? What are you using to examine the result?

I know because in order to test the result, I followed it up with the
following line and watched the output:

printf("Resulting number: %f\n", val);

Every number I entered that wasn't zero became a rediculously large
negative number.
Other than possibly silencing an obnoxious warning, the first cast is
irrelevant but again not harmful.

Why don't you create a char* and pass it's address so you can see
where the function is having a problem.

Worth a shot.
You have gone ballistic with casts. A cursory review shows that all
but two are absolutely unnecessary and I think the remaining two are
also unnecessary.

Unnecessary, but as you pointed out, not harmful. I cast everything
explicitly now, because I've lost count of the number of times where
I've written a program, and spent _days_ trying to find a bug caused by
the omission of a _seemingly_ unnecessary cast.
Of course, the fact that your code will not compile cleanly (undefined
identifier VERSION) doesn't lend any confidence that this is the
actual code you are talking about.

It is in fact the code I'm asking about. The VERSION identifier is
defined in the config.h file (which is automatically generated by the
configure shell script, and may vary depending on the system it's run on).

Note the three lines:
#if HAVE_CONFIG_H
Your entire program is only about 170 lines. Why didn't you paste it
in so we could see what you were talking about?

As you pointed out, the source file won't compile on it's own. It
requires a Makefile and configure.h, which are both generated by the
configure script.
- --
Regards,
Jonathan Lamothe

QOTD:

"Backups are for wimps. Real men upload their data to an FTP site and
have everyone else mirror it."

-- Linus Torvalds
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFCR1ioNrv4JaRC3JsRAjGAAJ9gAYc43lIkA0ba22zIoEVM4k9IQQCgzvZ0
lOubnEkcsJBQCm1cT7G6ltE=
=DWg7
-----END PGP SIGNATURE-----
 
J

Jens.Toerring

Jonathan Lamothe said:
I've written a program that requires the user to input a decimal number
which is then stored as a float.
First it's read into a variable buf which is an array of 16 characters
like this:
fgets(buf, 16, stdin);
I then tried to convert it into a float and store it in another variable.
val = strtof(buf, (char **)NULL);
For some reason, all numbers other than 0 are not being converted
properly. Am I doing something wrong?

I just created a small test program

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
char buf[ 16 ];
float val;

fgets( buf, 16, stdin );
val = strtof( buf, NULL );
printf( "%f\n", val );
return 0;
}

and it works just fine (compiled with gcc in C99 mode). So I can't
see anything from the code you posted that would keep it working
for you. And also the tempconv program from

ftp://linserv.homeip.net/src/tempconv

seems to work fine here after replacing strtod() to strtof() and
compiling it correctly, i.e. invoking gcc with the additional
option '-std=c99' to run it in C99 mode. So it doesn't seem to
be a C problem but one with gcc.

A few more comments concerning your tempconv program: you really go
over the top with your casts. E.g. "(float) 32" can also written as
"32.0" and is then much easier to read. And you don't need to cast
character constants like "(int) '\n'", contrary to what you might
expect character constants are always ints. The casts of the cases
in the switch constructs are also superfluous. Even the one before
the strlen() in read_string() isn't needed since you never get there
if the length of the input string is 0. While the casts here don't
hurt, they are rather annoying since normally a cast can be taken to
be a warning sign that at that place something unusual is going on
that may need careful checking. But by using lots and lots of unne-
cessary casts you cry "wolf" too often.

What you don't take into account is the user entering some invalid
numbers, which can nicely be dealt with by using the endptr argument
to strtof() and strtod()
Regards, Jens
 
J

Jonathan Lamothe

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I just created a small test program

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
char buf[ 16 ];
float val;

fgets( buf, 16, stdin );
val = strtof( buf, NULL );
printf( "%f\n", val );
return 0;
}

and it works just fine (compiled with gcc in C99 mode). So I can't
see anything from the code you posted that would keep it working
for you. And also the tempconv program from

ftp://linserv.homeip.net/src/tempconv

seems to work fine here after replacing strtod() to strtof() and
compiling it correctly, i.e. invoking gcc with the additional
option '-std=c99' to run it in C99 mode. So it doesn't seem to
be a C problem but one with gcc.

Maybe there's something wrong with my default libraries, then. :(
A few more comments concerning your tempconv program: you really go
over the top with your casts. E.g. "(float) 32" can also written as
"32.0" and is then much easier to read. And you don't need to cast
character constants like "(int) '\n'", contrary to what you might
expect character constants are always ints. The casts of the cases
in the switch constructs are also superfluous. Even the one before
the strlen() in read_string() isn't needed since you never get there
if the length of the input string is 0. While the casts here don't
hurt, they are rather annoying since normally a cast can be taken to
be a warning sign that at that place something unusual is going on
that may need careful checking. But by using lots and lots of unne-
cessary casts you cry "wolf" too often.

Yeah, I figured I was being a little over-paranoid with that. I've
already posted a second version where I've eliminated several
unnecessary casts.
What you don't take into account is the user entering some invalid
numbers, which can nicely be dealt with by using the endptr argument
to strtof() and strtod()

I kinda knew about that at the time. I'm just lazy. Maybe I'll deal
with that in the next release. ^^;
- --
Regards,
Jonathan Lamothe

QOTD:

"Backups are for wimps. Real men upload their data to an FTP site and
have everyone else mirror it."

-- Linus Torvalds
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFCR3G5Nrv4JaRC3JsRAtF4AJ9NUaFOixUPg7Pu96hLuf+y8ZFjTwCeMyZn
ENQ4hTrohp0C9UKQFkd8Cw4=
=gDZb
-----END PGP SIGNATURE-----
 
C

CBFalconer

Jonathan said:
Barry Schwarz wrote:
.... snip ...

Unnecessary, but as you pointed out, not harmful. I cast
everything explicitly now, because I've lost count of the number
of times where I've written a program, and spent _days_ trying to
find a bug caused by the omission of a _seemingly_ unnecessary
cast.

An extremely poor practice. Most casts serve only to prevent
needed error messages. You probably have too low a warning level
set. The objective is not to quiet the compiler, but to write
correct code. Variadic function parameters are an exception to
this rule.
 
K

Keith Thompson

Jonathan Lamothe said:
Well, it's not a typo and it compiles, so I'm going to go out on a limb
and say that I have a C99 compiler. It's whatever came with my copy of
Linux.

The strtof() function is defined by the runtime library, not by the
compiler. (The two together are part of the "implementation", which
is what the standard mostly refers to.)

What came with your copy of Linux is almost certainly gcc (the GNU
Compiler Collection, which includes a C compiler) and glibc, the GNU C
library.

A little experimentation on one of my Linux systems shows that the
strtof function is available in the library, but it doesn't appear in
<stdlib.h> *unless* you specify "-std=c99". (Without a declaration of
strtof in <stdlib.h>, I get incorrect results because there's an
implicit declaration as a function returning int; turning up the
warning level diagnoses the problem.)

gcc implements most, but not all, of C99; see
<http://gcc.gnu.org/c99status.html>.

It's common for pre-C99 implementations to provide some C99 features
as extensions (long long and "//" comments are particularly common).
 
O

Old Wolf

A few more comments concerning your tempconv program: you really go
over the top with your casts. E.g. "(float) 32" can also written as
"32.0" and is then much easier to read.

Minor nit: 32.0 is (double)32 . In this specific case there will
be no difference, but there would be if the number in question
were not representable exactly as a float.
 
O

Old Wolf

Jonathan said:
I cast everything explicitly now, because I've lost count of the
number of times where I've written a program, and spent _days_
trying to find a bug caused by the omission of a _seemingly_
unnecessary cast.

If I may use an analogy: that's like wearing full plate armour
to avoid getting your legs cut when you have to walk through
brambles. IMHO you would be better off to understand the
principle behind the bug. Then you would know in advance when
to use a cast, rather than using trial-and-error.

It sounds like the bugs you encountered were due to not grokking
the type of an expression; for example: double x = 3 / 4; (the
type of '3' and '4' is int, so the result still has type 'int'),
or: unsigned y = 1; int x = (-1 < y); (both operands must have the
same type, so -1 is converted to unsigned int before the comparison
occurs).
 
K

Keith Thompson

Old Wolf said:
If I may use an analogy: that's like wearing full plate armour
to avoid getting your legs cut when you have to walk through
brambles. IMHO you would be better off to understand the
principle behind the bug. Then you would know in advance when
to use a cast, rather than using trial-and-error.

Except that full plate armor really would protect you from
brambles.

A better analogy would be disconnecting the warning lights on your
car's dashboard rather than correcting the problem that caused the
lights to come on.

If your compiler gives you a diagnostic message, and adding a cast
turns off the message, the chances are good that the diagnostic was
correct, and the cast hasn't actually fixed anything.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,161
Messages
2,570,892
Members
47,427
Latest member
HildredDic

Latest Threads

Top