I want to read and write NaN values from/into text files using iostream and Visual C++. When writing a NaN value, i get 1.#QNAN. But, reading it back outputs 1.0 .
float nan = std::numeric_limits<float>::quiet_NaN ();
std::ofstream os("output.txt");
os << nan ;
os.close();
The output is 1.#QNAN .
std::ifstream is("output.txt");
is >> nan ;
is.close();
nan equals 1.0.
Solution
Finally, as suggested by awoodland, I've come up with this solution. I chose "nan" as a string representation of a NaN. Both << and >> operators are overridden.
using namespace ::std;
class NaNStream
{
public:
NaNStream(ostream& _out, istream& _in):out(_out), in(_in){}
template<typename T>
const NaNStream& operator<<(const T& v) const {out << v;return *this;}
template<typename T>
const NaNStream& operator>>(T& v) const {in >> v;return *this;}
protected:
ostream& out;
istream& in;
};
// override << operator for float type
template <> const NaNStream& NaNStream::operator<<(const float& v) const
{
// test whether v is NaN
if( v == v )
out << v;
else
out << "nan";
return *this;
}
// override >> operator for float type
template <> const NaNStream& NaNStream::operator>>(float& v) const
{
if (in >> v)
return *this;
in.clear();
std::string str;
if (!(in >> str))
return *this;
if (str == "nan")
v = std::numeric_limits<float>::quiet_NaN();
else
in.setstate(std::ios::badbit); // Whoops, we've still "stolen" the string
return *this;
}
A minimal working example: a finite float and a NaN are written into a stringstream and then read back.
int main(int,char**)
{
std::stringstream ss;
NaNStream nis(ss, ss);
nis << 1.5f << std::numeric_limits<float>::quiet_NaN ();
std::cout << ss.str() << std::endl; // OUTPUT : "1.5nan"
float a, b;
nis >> a; nis >> b;
std::cout << a << b << std::endl; // OUTPUT : "1.51.#QNAN"
}
American Standard Code for Information Interchange. ASCII Character Set. A char variable in C++ is a one-byte memory location where a single character value can be stored. Because one byte can hold values between 0 and 255 that means there are up to 256 different characters in the ASCII character set.
To insert an ASCII character, press and hold down ALT while typing the character code. For example, to insert the degree (º) symbol, press and hold down ALT while typing 0176 on the numeric keypad. You must use the numeric keypad to type the numbers, and not the keyboard.
When printing a float or double value to a std::ostream, the class template std::num_put<> is used (C++03 §22.2.2.2). It formats the value as if printed by printf with one of the %e, %E, %f, %g, or %G format specifiers, depending on the stream's flags (Table 58).
Likewise, when inputting a float or double value, it reads it as if with the scanf function with a format specifier of %g (§22.2.2.1.2/5).
So, the next question is why scanf does not properly parse 1.#QNAN. The C89 standard does not mention NaNs in its descriptions of both the fprintf and fscanf functions. It does say that the representation of floating-point numbers is unspecified, so this falls unspecified behavior.
C99, on the other hand, does specify the behavior here. For fprintf (C99 §7.19.6.1/8):
A
doubleargument representing an infinity is converted in one of the styles[-]infor[-]infinity— which style is implementation-defined. Adoubleargument representing a NaN is converted in one of the styles[-]nanor[-]nan(n-char-sequence)— which style, and the meaning of any n-char-sequence, is implementation-defined. The F conversion specifier producesINF,INFINITY, orNANinstead ofinf,infinity, ornan, respectively.243)
fscanf is specified to parse the number according to strtod(3) (C99 §7.19.6.2/12). strtod parses as follows (§7.20.1.3/3):
The expected form of the subject sequence is an optional plus or minus sign, then one of the following:
— a nonempty sequence of decimal digits optionally containing a decimal-point character, then an optional exponent part as defined in 6.4.4.2;
— a0xor0X, then a nonempty sequence of hexadecimal digits optionally containing a decimal-point character, then an optional binary exponent part as defined in 6.4.4.2;
—INForINFINITY, ignoring case
—NANorNAN(n-char-sequenceopt), ignoring case in the NAN part, where:n-char-sequence: digit nondigit n-char-sequence digit n-char-sequence nondigitThe subject sequence is defined as the longest initial subsequence of the input string, starting with the first non-white-space character, that is of the expected form. The subject sequence contains no characters if the input string is not of the expected form.
So, after taking all that in, the end result is that your C standard library is not C99-compliant, since 1.#QNAN is not a valid output of fprintf according to the above. But, it's well-known that Microsoft's C runtime is not C99-compliant, and it doesn't plan to become compliant any time soon, as far as I'm aware. Since C89 does not specify the behavior here with respect to NaNs, you're out of luck.
You could try switching to a different compiler and C runtime (such as Cygwin+GCC), but that's an awfully big hammer for such a small nail. If you really need this behavior, I'd recommend writing a wrapper class for floats that is capable of correctly formatting and parsing NaN values.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With