Float-to-string conversion that raises exception instead of returning0.0 like atof

T

Trond Valen

Hi!

Stupid atof, it returns 0.0 when it tries to parse something like
"fish". So I don't know whether the number was really 0 or a string that
couldn't be parsed. Is there a better way to do this?

Trond Valen
 
J

Jonathan Mcdougall

Trond said:
Hi!

Stupid atof, it returns 0.0 when it tries to parse something like
"fish". So I don't know whether the number was really 0 or a string that
couldn't be parsed. Is there a better way to do this?

# include <sstream>
# include <string>

class bad_float {};

float string_to_float(const std::string &s)
{
std::istringstream iss(s);
float f;
iss >> f;

if (!iss) throw bad_float();

return f;
}


Jonathan
 
D

deane_gavin

Trond said:
Hi!

Stupid atof, it returns 0.0 when it tries to parse something like
"fish". So I don't know whether the number was really 0 or a string that
couldn't be parsed. Is there a better way to do this?

Stupid indeed. But to answer your question: yes, there is a better way.
Use C++ stringstreams from <sstream> or use strtod (and strtol instead
of atoi) which can distinguish between zero and an error case.

Gavin Deane
 
T

Trond Valen

Jonathan said:
# include <sstream>
# include <string>

class bad_float {};

float string_to_float(const std::string &s)
{
std::istringstream iss(s);
float f;
iss >> f;

if (!iss) throw bad_float();

return f;
}


Jonathan

Thanks :)
 
D

deane_gavin

Jonathan said:
# include <sstream>
# include <string>

class bad_float {};

float string_to_float(const std::string &s)
{
std::istringstream iss(s);
float f;
iss >> f;

if (!iss) throw bad_float();

return f;
}

Better to use double rather than float. Despite its name, atof returns
a double rather than a float [*], so your function would need to be
changed to work with double to be a direct replacement.

[*] as far as I can work out from a bit of testing and research. I
don't have the C standard available for a definitive answer.

Gavin Deane
 
K

Krishanu Debnath

Jonathan said:
# include <sstream>
# include <string>

class bad_float {};

float string_to_float(const std::string &s)
{
std::istringstream iss(s);
float f;
iss >> f;

if (!iss) throw bad_float();

return f;
}

This doesn't take care invalid input like "1.04df". It cannot detect
overflow. strto* family functions are always a better choice unless you
are not doing toy programming.


Krishanu
 
B

Bob Hairgrove

Stupid indeed. But to answer your question: yes, there is a better way.
Use C++ stringstreams from <sstream>

It isn't quite that easy. For example, what does this program do? You
might expect it to throw an exception, but it doesn't:

#include <iostream>
#include <ostream>
#include <sstream>
#include <string>

int main() {
using namespace std;
istringstream ss(string("fish"));
double f = 0.0;
ss >> f;
cout << f << endl;
}
or use strtod (and strtol instead
of atoi) which can distinguish between zero and an error case.

This is much better IMHO.
 
D

deane_gavin

Bob said:
It isn't quite that easy. For example, what does this program do? You
might expect it to throw an exception, but it doesn't:

Why would I expect that? Apart from memory allocation, where is any
code that might throw?
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>

int main() {
using namespace std;
istringstream ss(string("fish"));
double f = 0.0;
ss >> f;
cout << f << endl;
}

Implicit in my statement that you can use streams is the requirement
that you know (or learn) *how* to use them. What your code is missing
is an important part of using any input stream (including, for example,
std::cin). That is, *every* time you read something, you should check
the stream state to see if it succeeded. So after ss >> f; you need
something like

if (!ss)
{
// failed to read a double
// report the error, perhaps by throwing an exception
}
This is much better IMHO.

For simple cases (like the OP's) it is perfectly sufficient.

Gavin Deane
 
B

Bob Hairgrove

Why would I expect that? Apart from memory allocation, where is any
code that might throw?

I meant "you" in a general, hypothetical sense ... just a sloppy
Americanism, I suppose. Here's a better version:

"One might expect it to throw an exception, but it doesn't:"
 
D

deane_gavin

Bob said:
I meant "you" in a general, hypothetical sense ... just a sloppy
Americanism, I suppose. Here's a better version:

"One might expect it to throw an exception, but it doesn't:"

By the same token, my response should have been

"Why would one expect that?"

Sometimes it would be easier if everyone could just read the words I
meant to write instead of the ones I actually wrote ;-)

Gavin Deane
 
J

Jacek Dziedzic

Jonathan said:
# include <sstream>
# include <string>

class bad_float {};

float string_to_float(const std::string &s)
{
std::istringstream iss(s);
float f;
iss >> f;

if (!iss) throw bad_float();

Wouldn't a successful read of the float from the
istringstream set the eof() and fail() flags of the
stream, thus making (!iss) true upon *success*?
 
R

red floyd

Jacek said:
Wouldn't a successful read of the float from the
istringstream set the eof() and fail() flags of the
stream, thus making (!iss) true upon *success*?

No. The eof() and fail() are only set upon an attempt to read past EOF.
 
M

Marcus Kwok

Krishanu Debnath said:
This doesn't take care invalid input like "1.04df". It cannot detect
overflow. strto* family functions are always a better choice unless you
are not doing toy programming.

This version (from the FAQ) can detect if there are extra characters:

class BadConversion : public std::runtime_error {
public:
BadConversion(const std::string& s)
: std::runtime_error(s)
{ }
};

inline double convertToDouble(const std::string& s,
bool failIfLeftoverChars = true)
{
std::istringstream i(s);
double x;
char c;
if (!(i >> x) || (failIfLeftoverChars && i.get(c)))
throw BadConversion("convertToDouble(\"" + s + "\")");
return x;
}
 
J

Jacek Dziedzic

red said:
No. The eof() and fail() are only set upon an attempt to read past EOF.

Are they? I thought they *may* either trigger upon reading past
EOF or up till EOF

http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5
has this to say:

"(...) the eof state may not get set until after a read is attempted
past the end of file. That is, reading the last byte from a file might
not set the eof state" (which I interpret as "reading the last byte
from a file may as well set the eof state").

Can somebody clarify?

- J.
 
R

red floyd

Jacek said:
Are they? I thought they *may* either trigger upon reading past
EOF or up till EOF

http://www.parashift.com/c++-faq-lite/input-output.html#faq-15.5
has this to say:

"(...) the eof state may not get set until after a read is attempted
past the end of file. That is, reading the last byte from a file might
not set the eof state" (which I interpret as "reading the last byte
from a file may as well set the eof state").

Can somebody clarify?

- J.

consider the following snippet:

std::istringstream is("123");
int i;
while (is >> i)
std::cout << i << std::endl;


If an istringstream was allowed to set eof() or fail() upon reading the
123, but before trying to read (or read past) the end of file/buffer,
then the semantics of the above snippet would be messed up, and you
wouldn't get the expected output of "123".
 
M

meagar

I have never known the EOF flag to be set until an attempt is made to
read past the end of the file. The classic example of how to read
until EOF with an istream depends on this behaviour:

int n;
while (cin >> n) {
// use N, knowing it contains actual input; the loops condition does
the EOF checking
}

If reading the last byte could "sometimes" cause (bool)cin to be false
(ie, EOF) then this logic totally breaks.
 
J

Jacek Dziedzic

meagar said:
I have never known the EOF flag to be set until an attempt is made to
read past the end of the file. The classic example of how to read
until EOF with an istream depends on this behaviour:

Well, running this code

#include <iostream>
#include <sstream>
using namespace std;

int main() {
istringstream ss("123");
int i;
ss >> i;
cout << ss.eof() << ss.fail() << endl;
}

produces "10" on my machine. Therefore I'd argue that EOF
was set here after having read the last datum in the stream.
int n;
while (cin >> n) {
// use N, knowing it contains actual input; the loops condition does
the EOF checking
}

this apparrently does not break, because fail() was not set.
If reading the last byte could "sometimes" cause (bool)cin to be false
(ie, EOF) then this logic totally breaks.

doesn't (bool)(cin >>n) check for (!good()) rather than eof()?

- J.
 
J

Jacek Dziedzic

red said:
consider the following snippet:

std::istringstream is("123");
int i;
while (is >> i)
std::cout << i << std::endl;


If an istringstream was allowed to set eof() or fail() upon reading the
123, but before trying to read (or read past) the end of file/buffer,
then the semantics of the above snippet would be messed up, and you
wouldn't get the expected output of "123".

The following code

#include <iostream>
#include <sstream>
using namespace std;

int main() {
istringstream ss("123");
int i;
ss >> i;
cout << ss.eof() << ss.fail() << endl;
}

produces "10" on my machine. Therefore I'd argue that inded
" an istringstream *was* allowed to set eof() or fail() upon
reading the 123 " (since eof() was set).

Your snippet still works, because while(is >> i) does not
care about eof() but fail(), at least that's how I understand it.

- J.
 

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

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,906
Latest member
SkinfixSkintag

Latest Threads

Top