By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,190 Members | 787 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,190 IT Pros & Developers. It's quick & easy.

Reading and Writing float value of infinity to file.

P: n/a
The output of the following program is:
1.#INF
1

But:
1.#INF
1.#INF

was expected and desired. How can I read a value of infinity from a stream?

#include <iostream>
#include <fstream>
#include <limits>

int main(void)
{
float Infinity;
Infinity = std::numeric_limits<float>::infinity();
std::cout << Infinity << "\n";

std::ofstream outf("test.txt");
if ( outf.is_open() )
outf << Infinity;

outf.close();

float Value = 0.0f;
std::ifstream inf("test.txt");
if ( inf.is_open() )
inf >Value;

inf.close();

std::cout << Value << "\n";

return 0;
}
Oct 19 '07 #1
Share this Question
Share on Google+
14 Replies


P: n/a
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
The output of the following program is:
1.#INF
1
But:
1.#INF
1.#INF
was expected and desired. How can I read a value of infinity
from a stream?
According to the current C++ standard, there is no infinity, so
you're in the realm of implementation defined, if not undefined
behavior. Presumably, however, the next version of the standard
will use C99 as the reference, rather than C90; in C99,
infinity is required to be output as either "inf" or "infinity",
and both should convert to infinity when read.

The current standard does not allow this behavior, however, nor
does the current draft, since they do not allow extraction of
"inf" when reading a number. So while the implementations I
usually use all output "inf", all report a read error when
attempting to read it back into a double. The only solution I
know of off hand is to read into a string, compare manually for
inf and nan, and use istringstream to convert the double if thos
tests fail.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Oct 19 '07 #2

P: n/a
On 2007-10-19 02:56:07 -0400, "Jim Langston" <ta*******@rocketmail.comsaid:
>
std::ofstream outf("test.txt");
if ( outf.is_open() )
outf << Infinity;

outf.close();
There's no newline at the end of "test.txt", so technically it's not a
valid text stream. No guarantees, but adding a newline might help.

--
Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com) Author of "The
Standard C++ Library Extensions: a Tutorial and Reference
(www.petebecker.com/tr1book)

Oct 19 '07 #3

P: n/a
"James Kanze" <ja*********@gmail.comwrote in message
news:11*********************@i13g2000prf.googlegro ups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
The output of the following program is:
1.#INF
1
But:
1.#INF
1.#INF
was expected and desired. How can I read a value of infinity
from a stream?
According to the current C++ standard, there is no infinity, so
you're in the realm of implementation defined, if not undefined
behavior. Presumably, however, the next version of the standard
will use C99 as the reference, rather than C90; in C99,
infinity is required to be output as either "inf" or "infinity",
and both should convert to infinity when read.

The current standard does not allow this behavior, however, nor
does the current draft, since they do not allow extraction of
"inf" when reading a number. So while the implementations I
usually use all output "inf", all report a read error when
attempting to read it back into a double. The only solution I
know of off hand is to read into a string, compare manually for
inf and nan, and use istringstream to convert the double if thos
tests fail.

---------------

Hmmm.. I was actually looking for a 9's rule number to indicate no value.
But it seems that infinity won't do it for me then, especially since I need
to read 3 of these values in a row.

I guess can I can research an ifstream implementation to read 1.#INF as
infinity, create my own class and override operator<<. It might be enough
in this specific implementation to read the number, if it's 1, peek at the
next value, if it's # then presume it's going to be #INF, read to string,
compare for #INF and if so load infinity. And then override operator float.

Let me see what I can come up with.
Oct 19 '07 #4

P: n/a

"Jim Langston" <ta*******@rocketmail.comwrote in message
news:Gc*************@newsfe02.lga...
"James Kanze" <ja*********@gmail.comwrote in message
news:11*********************@i13g2000prf.googlegro ups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
>The output of the following program is:
1.#INF
1
>But:
1.#INF
1.#INF
>was expected and desired. How can I read a value of infinity
from a stream?
[Snip discussion about C++ not supporing infinity]

Well, this is what I came up with. Output is as I want. Do you see
anything I'm doing wrong here? Of course I'll have to come up with a better
name than "MyFloat".

1.#INF
1 2.2 3.3
1.#INF 2.2 3.3

#include <iostream>
#include <fstream>
#include <limits>
#include <string>

class MyFloat
{
public:
MyFloat( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};

std::istream& operator>>( std::istream& is, MyFloat& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}

return is;
}

int main(void)
{
float Infinity, Foo = 2.2f, Bar = 3.3f;
Infinity = std::numeric_limits<float>::infinity();
std::cout << Infinity << "\n";

std::ofstream outf("test.txt");
if ( outf.is_open() )
outf << Infinity << " " << Foo << " " << Bar << "\n";

outf.close();

float Value = 0.0f, Value2 = 0.0f, Value3 = 0.0f;
std::ifstream inf("test.txt");
if ( inf.is_open() )
inf >Value >Foo >Bar;

inf.close();

std::cout << Value << " " << Foo << " " << Bar << "\n";

MyFloat MyValue = 0.0f, MyValue2 = 0.0f, MyValue3 = 0.0f;
std::ifstream inf2("test.txt");
if ( inf2.is_open() )
inf2 >MyValue >MyValue2 >MyValue3;

inf2.close();

std::cout << MyValue << " " << MyValue2 << " " << MyValue3 << "\n";

return 0;
}
Oct 19 '07 #5

P: n/a
"Jim Langston" <ta*******@rocketmail.comwrote in message
news:0o***********@newsfe06.lga...
>
"Jim Langston" <ta*******@rocketmail.comwrote in message
news:Gc*************@newsfe02.lga...
>"James Kanze" <ja*********@gmail.comwrote in message
news:11*********************@i13g2000prf.googlegr oups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
>>The output of the following program is:
1.#INF
1
>>But:
1.#INF
1.#INF
>>was expected and desired. How can I read a value of infinity
from a stream?
[Snip discussion about C++ not supporing infinity]
Well, this is what I think I'll put into production, unless anyone can show
me any flaws in it. It seems to work fine as a float in my rudimentary
tests.

namespace jml
{
class Float
{
public:
Float( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};

std::istream& operator>>( std::istream& is, Float& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}

return is;
}

};

I'll probably make Value private just because.
Oct 19 '07 #6

P: n/a
Jim Langston wrote:
[..]
Well, this is what I think I'll put into production, unless anyone
can show me any flaws in it. It seems to work fine as a float in my
rudimentary tests.

namespace jml
{
class Float
{
public:
Float( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};

std::istream& operator>>( std::istream& is, Float& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}

return is;
}

};

I'll probably make Value private just because.
Actually, I'd probably get rid of 'operator float()' in it (besides,
it ought to be declared 'const' if you ask me), and always use the
explicit ".Value", or have a function called "value()" in it instead:

class Float
{
...
float value() const { return Value; }
private:
float Value;
};

Recently in our development we ran into some implicit conversions
that we didn't want, and it was all due to type conversion functions
(which BTW you can't declare "explicit"). We decided we didn't like
those in our own types.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Oct 19 '07 #7

P: n/a
On Oct 19, 2:26 pm, "Jim Langston" <tazmas...@rocketmail.comwrote:
"Jim Langston" <tazmas...@rocketmail.comwrote in message
news:Gc*************@newsfe02.lga..."James Kanze"
<james.ka...@gmail.comwrote in message
news:11*********************@i13g2000prf.googlegro ups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
The output of the following program is:
1.#INF
1
But:
1.#INF
1.#INF
was expected and desired. How can I read a value of infinity
from a stream?
[Snip discussion about C++ not supporing infinity]
Well, this is what I came up with. Output is as I want. Do
you see anything I'm doing wrong here? Of course I'll have to
come up with a better name than "MyFloat".
1.#INF
1 2.2 3.3
1.#INF 2.2 3.3
#include <iostream>
#include <fstream>
#include <limits>
#include <string>
class MyFloat
{
public:
MyFloat( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};
std::istream& operator>>( std::istream& is, MyFloat& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}
return is;
}
I'm not sure I like that. You've built in knowledge of how your
implementation formats infinity, and I'll bet that this format
is not guaranteed. I'd do something more along the lines of:

std::istream&
operator>>( std::istream& in, MyFloat& out )
{
std::string s ;
in >s ;
if ( in ) {
static std::string const
infRepresentation =
initInfRepresentation() ;
if ( s == infRepresentation ) {
out.value = std::numeric_limits< float >::infinity() ;
} else {
std::istringstream t( s ) ;
t >out.value ;
}
}
return in ;
}

with:

std::string
initInfRepresentation()
{
std::ostringstream t ;
t << std::numeric_limits< float >::infinity() ;
return t.string() ;
}

Actually, I'd go even further, since I'd want to handle at
least positive and negative infinity, and I'd probably make the
comparison case insensitive. But you get the idea.

And of course, regardless of the solution, you have to be aware
that it will break anytime the compiler changes its
representation of infinity. Which in your case seems almost
inevitable, given that the C99 standard requires "[+-]inf" or
"[+-]infinity", and that this requirement will almost certainly
be part of the next version of the C++ standard. So if you want
any kind of portability, you really have to use your MyFloat for
both output and input.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Oct 20 '07 #8

P: n/a
"James Kanze" <ja*********@gmail.comwrote in message
news:11**********************@q3g2000prf.googlegro ups.com...
On Oct 19, 2:26 pm, "Jim Langston" <tazmas...@rocketmail.comwrote:
"Jim Langston" <tazmas...@rocketmail.comwrote in message
news:Gc*************@newsfe02.lga..."James Kanze"
<james.ka...@gmail.comwrote in message
>news:11*********************@i13g2000prf.googlegr oups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
>The output of the following program is:
>1.#INF
>1
>But:
>1.#INF
>1.#INF
was expected and desired. How can I read a value of infinity
from a stream?
[Snip discussion about C++ not supporing infinity]
Well, this is what I came up with. Output is as I want. Do
you see anything I'm doing wrong here? Of course I'll have to
come up with a better name than "MyFloat".
1.#INF
1 2.2 3.3
1.#INF 2.2 3.3
#include <iostream>
#include <fstream>
#include <limits>
#include <string>
class MyFloat
{
public:
MyFloat( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};
std::istream& operator>>( std::istream& is, MyFloat& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}
return is;
}

I'm not sure I like that. You've built in knowledge of how your
implementation formats infinity, and I'll bet that this format
is not guaranteed. I'd do something more along the lines of:

std::istream&
operator>>( std::istream& in, MyFloat& out )
{
std::string s ;
in >s ;
One thing here, though, is you read a string from istream. This could put
is in a different state then if this operator>wasn't used. For example.
Say what is waiting is:
123.45And that's all.

Using a pure is >float
"And that's all" would be waiting in the stream. With this function this
way, however, "And that's all" will be thrown away.

The way I did it it would only be thrown away if the first character was #,
not as likely an occurance (but adminttedly still could happen).

Other than that, I agree with your assessment. But I don't like putting the
stream in a state that would be different if my function wasn't used.
if ( in ) {
static std::string const
infRepresentation =
initInfRepresentation() ;
if ( s == infRepresentation ) {
out.value = std::numeric_limits< float >::infinity() ;
} else {
std::istringstream t( s ) ;
t >out.value ;
}
}
return in ;
}

with:

std::string
initInfRepresentation()
{
std::ostringstream t ;
t << std::numeric_limits< float >::infinity() ;
return t.string() ;
}

Actually, I'd go even further, since I'd want to handle at
least positive and negative infinity, and I'd probably make the
comparison case insensitive. But you get the idea.

And of course, regardless of the solution, you have to be aware
that it will break anytime the compiler changes its
representation of infinity. Which in your case seems almost
inevitable, given that the C99 standard requires "[+-]inf" or
"[+-]infinity", and that this requirement will almost certainly
be part of the next version of the C++ standard. So if you want
any kind of portability, you really have to use your MyFloat for
both output and input.

Oct 20 '07 #9

P: n/a
"Jim Langston" <ta*******@rocketmail.comwrote in message
news:1c*************@newsfe06.lga...
>"James Kanze" <ja*********@gmail.comwrote in message
news:11**********************@q3g2000prf.googlegr oups.com...
On Oct 19, 2:26 pm, "Jim Langston" <tazmas...@rocketmail.comwrote:
"Jim Langston" <tazmas...@rocketmail.comwrote in message
news:Gc*************@newsfe02.lga..."James Kanze"
<james.ka...@gmail.comwrote in message
news:11*********************@i13g2000prf.googlegr oups.com...
On Oct 19, 8:56 am, "Jim Langston" <tazmas...@rocketmail.comwrote:
The output of the following program is:
1.#INF
1
>But:
1.#INF
1.#INF

was expected and desired. How can I read a value of infinity
from a stream?

[Snip discussion about C++ not supporing infinity]

Well, this is what I came up with. Output is as I want. Do
you see anything I'm doing wrong here? Of course I'll have to
come up with a better name than "MyFloat".
1.#INF
1 2.2 3.3
1.#INF 2.2 3.3
#include <iostream>
#include <fstream>
#include <limits>
#include <string>
class MyFloat
{
public:
MyFloat( float Value = 0.0f ): Value( Value ) {}
operator float() { return Value; }
float Value;
};
std::istream& operator>>( std::istream& is, MyFloat& mf )
{
if ( is >mf.Value )
{
if ( mf.Value == 1.0f && is.peek() == '#' )
{
std::string Rest;
is >Rest;
if ( Rest == "#INF" )
mf.Value = std::numeric_limits<float>::infinity();
}
}
return is;
}

I'm not sure I like that. You've built in knowledge of how your
implementation formats infinity, and I'll bet that this format
is not guaranteed. I'd do something more along the lines of:

std::istream&
operator>>( std::istream& in, MyFloat& out )
{
std::string s ;
in >s ;

One thing here, though, is you read a string from istream. This could put
is in a different state then if this operator>wasn't used. For example.
Say what is waiting is:
123.45And that's all.

Using a pure is >float
"And that's all" would be waiting in the stream. With this function this
way, however, "And that's all" will be thrown away.
Actually, "And" would be thrown away.
>
The way I did it it would only be thrown away if the first character was
#, not as likely an occurance (but adminttedly still could happen).

Other than that, I agree with your assessment. But I don't like putting
the stream in a state that would be different if my function wasn't used.
> if ( in ) {
static std::string const
infRepresentation =
initInfRepresentation() ;
if ( s == infRepresentation ) {
out.value = std::numeric_limits< float >::infinity() ;
} else {
std::istringstream t( s ) ;
t >out.value ;
}
}
return in ;
}

with:

std::string
initInfRepresentation()
{
std::ostringstream t ;
t << std::numeric_limits< float >::infinity() ;
return t.string() ;
}

Actually, I'd go even further, since I'd want to handle at
least positive and negative infinity, and I'd probably make the
comparison case insensitive. But you get the idea.

And of course, regardless of the solution, you have to be aware
that it will break anytime the compiler changes its
representation of infinity. Which in your case seems almost
inevitable, given that the C99 standard requires "[+-]inf" or
"[+-]infinity", and that this requirement will almost certainly
be part of the next version of the C++ standard. So if you want
any kind of portability, you really have to use your MyFloat for
both output and input.


Oct 20 '07 #10

P: n/a
James Kanze <ja*********@gmail.comwrites:
I'm not really sure what the C standard says here---C99
requires, for example, that the string "Infinity" be recognized
and read as a valid float or double, e.g. when matching a "%f"
or "%lf" in a scanf. What happens when it tries to match
"Infinitx" to a "%f". I don't see any way that it could be made
to work so that you don't extract some characters which aren't
then consumed as part of a value. (Remember that "Inf" *is*
also a legitimate float/double value in C. So given the above,
and a scanf format like "%f%s", the float should contain
infinity, and the string "initx". And of course, only one
character push back/look ahead is allowed.) I think that
there's a fundamental problem involved, without any real
solution.
C99 clarified the behavior of fscanf, with the following sentence
and footnote:

An input item is defined as the longest sequence of input
characters which does not exceed any specified field width
and which is, or is a prefix of, a matching input
sequence.242)

242) fscanf pushes back at most one input character onto the
input stream. Therefore, some sequences that are
acceptable to strtod, strtol, etc., are unacceptable to
fscanf.
--
"...dans ce pays-ci il est bon de tuer de temps en temps un amiral
pour encourager les autres."
--Voltaire, _Candide_
Oct 21 '07 #11

P: n/a
"Ben Pfaff" <bl*@cs.stanford.eduwrote in message
news:87************@blp.benpfaff.org...
James Kanze <ja*********@gmail.comwrites:
>I'm not really sure what the C standard says here---C99
requires, for example, that the string "Infinity" be recognized
and read as a valid float or double, e.g. when matching a "%f"
or "%lf" in a scanf. What happens when it tries to match
"Infinitx" to a "%f". I don't see any way that it could be made
to work so that you don't extract some characters which aren't
then consumed as part of a value. (Remember that "Inf" *is*
also a legitimate float/double value in C. So given the above,
and a scanf format like "%f%s", the float should contain
infinity, and the string "initx". And of course, only one
character push back/look ahead is allowed.) I think that
there's a fundamental problem involved, without any real
solution.

C99 clarified the behavior of fscanf, with the following sentence
and footnote:

An input item is defined as the longest sequence of input
characters which does not exceed any specified field width
and which is, or is a prefix of, a matching input
sequence.242)

242) fscanf pushes back at most one input character onto the
input stream. Therefore, some sequences that are
acceptable to strtod, strtol, etc., are unacceptable to
fscanf.
If the standard says that (and you say it does, so I don't doubt it) then I
have no problem with Jame's solution. If the standard disallows some
sequences as unacceptable then I think that using Infinity and leaving the
stream in some undefined state is acceptable (undefined as to how different
compilers may do it when C++Ox comes out).
Oct 22 '07 #12

P: n/a
On Oct 21, 10:38 pm, Ben Pfaff <b...@cs.stanford.eduwrote:
James Kanze <james.ka...@gmail.comwrites:
I'm not really sure what the C standard says here---C99
requires, for example, that the string "Infinity" be recognized
and read as a valid float or double, e.g. when matching a "%f"
or "%lf" in a scanf. What happens when it tries to match
"Infinitx" to a "%f". I don't see any way that it could be made
to work so that you don't extract some characters which aren't
then consumed as part of a value. (Remember that "Inf" *is*
also a legitimate float/double value in C. So given the above,
and a scanf format like "%f%s", the float should contain
infinity, and the string "initx". And of course, only one
character push back/look ahead is allowed.) I think that
there's a fundamental problem involved, without any real
solution.
C99 clarified the behavior of fscanf, with the following sentence
and footnote:
An input item is defined as the longest sequence of input
characters which does not exceed any specified field width
and which is, or is a prefix of, a matching input
sequence.242)
242) fscanf pushes back at most one input character onto the
input stream. Therefore, some sequences that are
acceptable to strtod, strtol, etc., are unacceptable to
fscanf.
I'm not too sure about the footnote (which is non-normative),
but if I understand the phrase correctly, given

sscanf( "Infinitx", "%f%s", &aFloat, aCharBuffer ) ;

the library would put infinity in aFloat, and "x" in
aCharBuffer.

As I said, it's not an easy problem, and there's no good
solution. About the only way to handle this sort of thing
correctly is to read a complete line, then use regular
expressions to match what looks like a floating point, i.e.
something like
"[+-](" "([0-9]+\.[0-9]*([Ee][+-]?[0-9]+)?)"
"|(\.[0-9]+([Ee][+-]?[0-9]+)?)"
"|([0-9]+[Ee][+-]?[0-9]+)"
"|([Ii][Nn][Ff])"
"|([Ii][Nn][Ff][Ii][Nn][Ii][Tt][Yy])"
")"
(The exact syntax will depend on the regular expression class
you use. The above corresponds to my own regular expression
class---which is probably not the one you use, or even should be
using.)

Any reasonable regular expression class will allow you to obtain
the end point of the match; you then use that and the starting
point to create a string which initializes an istringsream,
which then converts the input. This works, of course, only
because you can easily multiply scan in a string; the look-ahead
needed when matching Infinitx, for example, doesn't matter,
because when you do the actual conversion, you'll reread from
the start anyway, and only read the characters which were really
part of the match.

Note that for a seekable input source, you can do this with my
RegularExpression class (which itself works with input
iterators, although there's not often much you can do with the
output in such cases) as well: memorize the current position,
break up the different cases above, try for the match each time,
reseeking to the original position in case of failure; for
either "inf" or "infinity", just store the results; for anything
else, >into a double&.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Oct 22 '07 #13

P: n/a
James Kanze <ja*********@gmail.comwrites:
On Oct 21, 10:38 pm, Ben Pfaff <b...@cs.stanford.eduwrote:
>C99 clarified the behavior of fscanf, with the following sentence
and footnote:
> An input item is defined as the longest sequence of input
characters which does not exceed any specified field width
and which is, or is a prefix of, a matching input
sequence.242)
> 242) fscanf pushes back at most one input character onto the
input stream. Therefore, some sequences that are
acceptable to strtod, strtol, etc., are unacceptable to
fscanf.

I'm not too sure about the footnote (which is non-normative),
but if I understand the phrase correctly, given

sscanf( "Infinitx", "%f%s", &aFloat, aCharBuffer ) ;

the library would put infinity in aFloat, and "x" in
aCharBuffer.
I interpreted this the opposite way: that the library's attempt
to match "Infinity" would fail at 'x' and therefore push back 'x'
and return 0, having consumed "Infinit".

Not that it matters much.
--
Ben Pfaff
http://benpfaff.org
Oct 22 '07 #14

P: n/a
On Oct 22, 5:26 pm, Ben Pfaff <b...@cs.stanford.eduwrote:
James Kanze <james.ka...@gmail.comwrites:
On Oct 21, 10:38 pm, Ben Pfaff <b...@cs.stanford.eduwrote:
C99 clarified the behavior of fscanf, with the following sentence
and footnote:
An input item is defined as the longest sequence of input
characters which does not exceed any specified field width
and which is, or is a prefix of, a matching input
sequence.242)
242) fscanf pushes back at most one input character onto the
input stream. Therefore, some sequences that are
acceptable to strtod, strtol, etc., are unacceptable to
fscanf.
I'm not too sure about the footnote (which is non-normative),
but if I understand the phrase correctly, given
sscanf( "Infinitx", "%f%s", &aFloat, aCharBuffer ) ;
the library would put infinity in aFloat, and "x" in
aCharBuffer.
I interpreted this the opposite way: that the library's attempt
to match "Infinity" would fail at 'x' and therefore push back 'x'
and return 0, having consumed "Infinit".
This is the point where I'm not sure. From the above phrase, it
seems clear that the "%f" specifier would consume "Infinit".
The question is then whether this sequence is an error. There
is a statement (§7.19.6.2/10): "If the input item is not a
matching sequence, the execution of the directive fails: this
condition is a matching failure." But what does it really mean
by "matching sequence"; for "%f", all it says is: "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 strtod function accepts "Infinit" without
problems.

But you're probably right, since in the description of stdtod,
the term "subject sequence" is also used, and given "Infinit" as
input, the "subject sequence" for stdtod is "Inf", which is not
the sequence which was extracted.

This would solve Jim Langston's problem nicely. In his
conversion operator, having gotten past any leading white space
and the sign, he can then do a switch on the next character:
[0-9.], and the usual floating point conversion can be used
(he's already extracted the sign, but that's easy to handle
after the conversion); [Ii], and he advances, extracting the
characters, trying to match "infinity"---if the resulting string
matches "inf" or "infinity", that's the results, otherwise, it's
an error; and for [Nn], he does the same thing for "nan" (not
forgetting the n-char-sequence, if he wants to be strictly
conform with C---but then, he also has to handle the hex
format, since his C++ implementation almost certainly won't).

Just for the fun of it, I whipped up a simple implementation
here. It doesn't handle all of the formatting flags correctly,
and it supposes a C99 compliant math.h, but it should be a good
starting point:

class FloatWithInfIn
{
public:
FloatWithInfIn( float& f )
: myValue( &f )
{
}

friend std::ostream&operator<<( std::ostream& dest,
FloatWithInfIn const& src ) ;
friend std::istream&operator>>( std::istream& source,
FloatWithInfIn const& dest ) ;

private:
float* myValue ;
} ;

class FloatWithInfOut
{
public:
FloatWithInfOut( float const& f )
: myValue( &f )
{
}

friend std::ostream&operator<<( std::ostream& dest,
FloatWithInfOut const& src ) ;

private:
float const* myValue ;
} ;

std::ostream&
outputFloatWithInf(
std::ostream& dest,
float const& value )
{
switch ( fpclassify( value ) ) {
case FP_INFINITE :
if ( value < 0.0 ) {
dest << '-' ;
} else if ( (dest.flags() & std::ios::showpos) != 0 ) {
dest << '+' ;
}
dest << "Infinity" ;
break ;

case FP_NAN :
dest << "NaN" ;
break ;

default :
dest << value ;
break ;
}
return dest ;
}

inline std::ostream&
operator<<(
std::ostream& dest,
FloatWithInfOut const&
value )
{
return outputFloatWithInf( dest, *value.myValue ) ;
}

inline std::ostream&
operator<<(
std::ostream& dest,
FloatWithInfIn const&
value )
{
return outputFloatWithInf( dest, *value.myValue ) ;
}

void
getInfinity(
std::istream& source,
float& dest )
{
static std::string const
inf( "infinity" ) ;
std::ctype< char const&
ctype
= std::use_facet< std::ctype< char >
>( source.getloc() ) ;
std::string extracted ;
std::string::const_iterator
pos = inf.begin() ;
while ( pos != inf.end() && ctype.tolower( source.peek() ) ==
*pos ) {
extracted += ctype.tolower( source.get() ) ;
++ pos ;
}
if ( extracted != "inf" && extracted != "infinity" ) {
source.setstate( std::ios::failbit ) ;
} else {
dest = std::numeric_limits< float >::infinity() ;
}
}

void
getNaN(
std::istream& source,
float& dest )
{
static std::string const
nan( "nan" ) ;
std::ctype< char const&
ctype
= std::use_facet< std::ctype< char >
>( source.getloc() ) ;
std::string extracted ;
std::string::const_iterator
pos = nan.begin() ;
while ( pos != nan.end() && ctype.tolower( source.peek() ) ==
*pos ) {
extracted += ctype.tolower( source.get() ) ;
++ pos ;
}
if ( extracted != "nan" ) {
source.setstate( std::ios::failbit ) ;
} else {
// Or maybe only characters in the basic character set
// should be allowed here.
while ( ctype.is( std::ctype_base::alnum, source.peek() )
|| source.peek() == '_' ) {
source.get() ;
}
dest = std::numeric_limits< float >::quiet_NaN() ;
}
}

std::istream&
operator>>(
std::istream& source,
FloatWithInfIn const&
dest )
{
if ( (source.flags() & std::ios::skipws) != 0 ) {
source >std::ws ;
}
char sign
= ( source.peek() == '-' || source.peek() == '+'
? source.get()
: '+' ) ;
float result = 0.0 ;
switch ( source.peek() ) {
case 'I' :
case 'i' :
getInfinity( source, result ) ;
break ;

case 'N' :
case 'n' :
getNaN( source, result ) ;
break ;

default :
{
// Should use RAII for this, since >can throw...
std::ios::fmtflags flags = source.flags() ;
source.unsetf( std::ios::skipws ) ;
source >result ;
source.flags( flags ) ;
}
break ;
}
if ( source ) {
*dest.myValue = ( sign == '-'
? - result
: result ) ;
}
return source ;
}

FloatWithInfIn
floatWithInf(
float& f )
{
return FloatWithInfIn( f ) ;
}

FloatWithInfOut
floatWithInf(
float const& f )
{
return FloatWithInfOut( f ) ;
}

(Note the use of a function to automatically choose, according
to lvalue-ness and const-ness, whether we can do just output, or
both.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Oct 23 '07 #15

This discussion thread is closed

Replies have been disabled for this discussion.