18

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"
}
mskfisher
  • 3,291
  • 4
  • 35
  • 48
Mourad
  • 404
  • 4
  • 11
  • 4
    The question should be, "How to do formatted I/O with NaN". I suppose. Good question. – Kerrek SB Sep 19 '11 at 22:24
  • http://pubs.opengroup.org/onlinepubs/007904975/functions/scanf.html says `If the fprintf() family of functions generates character string representations for infinity and NaN (a symbolic entity encoded in floating-point format) to support IEEE Std 754-1985, the fscanf() family of functions shall recognize them as input.` for whatever that's worth. – Mooing Duck Sep 19 '11 at 22:31
  • @Moo: if that's true, then at least we know now that iostreams don't use `fscanf` :-) In any case that'd be a ridiculous thing to rely on, since the textual representation of NaN varies from compiler to compiler, and probably from state to state, too. Maybe it's just not possible to *read* NaN. (There isn't even a literal for it, coming to think of it.) – Kerrek SB Sep 19 '11 at 22:34
  • 2
    @KerrekSB: `"NaN"_my_super_duper_literal_operator` :P – R. Martinho Fernandes Sep 19 '11 at 22:39
  • @RMF: Show me **one** compiler that implements overloading the literal operator! – Kerrek SB Sep 19 '11 at 22:45
  • 1
    @Mooing Duck: Note that that documentation references the C99 standard (aka ISO/IEC 9899:1999), to which, as I mentioned in my answer, Microsoft's runtime does not conform. – Adam Rosenfield Sep 20 '11 at 23:57
  • @MooingDuck: What characters are supposed to be consumed or not consumed if an input stream contains something whose first character or two resemble a representation of NaN or infinity? – supercat Feb 14 '14 at 18:09
  • @supercat: I have absolutely no idea. Good question! – Mooing Duck Feb 14 '14 at 20:58

2 Answers2

16

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 double argument representing an infinity is converted in one of the styles [-]inf or [-]infinity — which style is implementation-defined. A double argument representing a NaN is converted in one of the styles [-]nan or [-]nan(n-char-sequence) — which style, and the meaning of any n-char-sequence, is implementation-defined. The F conversion specifier produces INF, INFINITY, or NAN instead of inf, infinity, or nan, 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;
— a 0x or 0X, 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;
INF or INFINITY, ignoring case
NAN or NAN(n-char-sequenceopt), ignoring case in the NAN part, where:

n-char-sequence:
    digit
    nondigit
    n-char-sequence digit
    n-char-sequence nondigit
The 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.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
7

With C++03 you can fairly easily work around the issue with the aid of a helper class and your own operator:

#include <iostream>
#include <sstream>
#include <string>
#include <limits>

struct FloatNaNHelper {
  float value;
  operator const float&() const { return value; }
};

std::istream& operator>>(std::istream& in, FloatNaNHelper& f) {
  if (in >> f.value)
    return in;

  in.clear();
  std::string str;
  if (!(in >> str))
    return in;

  // use std::transform for lowercaseness?
  // NaN on my platform is written like this.
  if (str == "NaN")
    f.value = std::numeric_limits<float>::quiet_NaN();
  else
    in.setstate(std::ios::badbit); // Whoops, we've still "stolen" the string

  return in;
}

This works for NaN on my platform quite sensibly, but also illustrates the portability problem inherent there too - your library seems to represent it differently, which could complicate the issue somewhat if you wanted to support both. I used this test with it:

int main() {
  std::istringstream in("1.0 555 NaN foo");
  FloatNaNHelper f1,f2,f3;
  in >> f1 >> f2 >> f3;
  std::cout << static_cast<float>(f1) << ", " << static_cast<float>(f2) << ", " << static_cast<float>(f3) << std::endl;

  if (in >> f1)
    std::cout << "OOPS!" << std::endl;
}

You can also change the semantics of this to something possibly a little cleaner:

int main() {
  std::istringstream in("1.0 555 NaN foo");
  float f1,f2,f3;
  in >> FloatNaNHelper(f1) >> FloatNaNHelper(f2) >> FloatNaNHelper(f3);
  std::cout << f1 << ", " << f2 << ", " << f3 << std::endl;
}

Requires the changing FloatNaNNHelper:

struct FloatNaNHelper {
  float& value;
  explicit FloatNaNHelper(float& f) : value(f) { }
};

And the operator:

std::istream& operator>>(std::istream& in, const FloatNaNHelper& f);
Flexo
  • 87,323
  • 22
  • 191
  • 272