49

I have some input to be read by a input filestream (for example):

-365.269511 -0.356123 -Inf 0.000000

When I use std::ifstream mystream; to read from the file to some

double d1 = -1, d2 = -1, d3 = -1, d4 = -1;

(assume mystream has already been opened and the file is valid),

mystream >> d1 >> d2 >> d3 >> d4;

mystream is in the fail state. I would expect

std::cout << d1 << " " << d2 << " " << d3 << " " << d4 << std::endl;

to output

-365.269511 -0.356123 -1 -1. I would want it to output -365.269511 -0.356123 -Inf 0 instead.

This set of data was output using C++ streams. Why can't I do the reverse process (read in my output)? How can I get the functionality I seek?

From MooingDuck:

#include <iostream>
#include <limits>

using namespace std;

int main()
{
  double myd = std::numeric_limits<double>::infinity();
  cout << myd << '\n';
  cin >> myd;
  cout << cin.good() << ":" << myd << endl;
  return 0;
}

Input: inf

Output:

inf
0:inf

See also: http://ideone.com/jVvei

Also related to this problem is NaN parsing, even though I do not give examples for it.

I added to the accepted answer a complete solution on ideone. It also includes paring for "Inf" and "nan", some possible variations to those keywords that may come from other programs, such as MatLab.

Drise
  • 4,310
  • 5
  • 41
  • 66

6 Answers6

13

Update Provided a simple test case that shows that Boost Spirit is capable to handle all varieties of special values in this area. See below: Boost Spirit (FTW).

The standard

The only normative information in this area that I've been able to find is in sections 7.19.6.1/7.19.6.2 of the C99 standard.

Sadly, the corresponding sections of the latest C++ standard document (n3337.pdf) doesn't appear to specify support for infinity, inf and or NaN in the same way. (Perhaps I'm missing a footnote that refers to the C99/C11 spec?)

The library implementors

In 2000, the Apache libstdcxx received a bug report stating

The num_get<> facet's do_get() members fail to take the special strings [-]inf[inity] and [-]nan into account. The facet reports an error when it encounters such strings. See 7.19.6.1 and 7.19.6.2 of C99 for a list of allowed strings.

However the subsequent discussion yielded that (at least with named locale-s) it would actually be illegal for an implementation to parse special values:

The characters in the lookup table are "0123456789abcdefABCDEF+-". Library issue 221 would amend that to "0123456789abcdefxABCDEFX+-". "N" isn't present in the lookup table, so stage 2 of num_get<>::do_get() is not permitted to read the character sequence "NaN".

Other resources

securecoding.cert.org clearly states that the following 'Compliant Code' is required to avoid parsing infinity or NaN. This implies, that some implementations actually support that - assuming the author ever tested the published code.

#include <cmath>

float currentBalance; /* User's cash balance */

void doDeposit() {
  float val;

  std::cin >> val;
  if (std::isinf(val)) {
    // handle infinity error
  }
  if (std::isnan(val)) {
    // handle NaN error
  }
  if (val >= MaxValue - currentBalance) {
    // Handle range error
  }

  currentBalance += val;
}

Boost Spirit (FTW)

The following trivial example has the desired output:

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;

int main()
{
    const std::string input = "3.14 -inf +inf NaN -NaN +NaN 42";

    std::vector<double> data;
    std::string::const_iterator f(input.begin()), l(input.end());

    bool ok = qi::parse(f,l,qi::double_ % ' ',data);

    for(auto d : data)
        std::cout << d << '\n';
}

Output:

3.14
-inf
inf
nan
-nan
nan
42

Summary/TL;DR

I'm inclined to say that C99 specifies the behaviour for *printf/*scanf to include infinity and NaN. C++11, sadly appears to not specify it (or even prohibit it, in the presence of named locales).

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 2
    I'd be more than "inclined to say". The correct part of C99 to look at is §7.20.1.3, which is clear and unambiguous in requiring support for reading "inf" or "infinity" as signifying infinity, and "nan" or "nan" as signifying a NaN (all of them case insensitive, with `` basically meaning meaning an arbitrary sequence of input. – Jerry Coffin Aug 01 '12 at 22:02
  • 2
    @JerryCoffin your `` is not arbitrary. It's specified as `NAN(n-char-sequence_opt)`, i.e. the arbitrary part only consists of digits and non-digits, and the parentheses are mandatory. Otherwise e.g. `nanometer` would be read as NaN, which is ridiculous. – Ruslan Jul 14 '15 at 12:57
9

Write a function with a signature like this:

std::istream & ReadDouble(std::istream & is, double & d);

Inside it, you:

  1. Read a string from the stream using operator>>
  2. Try to convert the string to a double using one of various methods. std::stod, boost::lexical_cast, etc...
  3. If the conversion succeeds, set the double and return the stream.
  4. If the conversion fails, test the string for equality with "inf" or "INF" or whatever.
  5. If the test passes, set the double to infinity and return the stream, else:
  6. If the test fails, set the fail bit on the stream and return it.
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
9

Edit: To avoid the use of a wrapper structure around a double, I enclose an istream within a wrapper class instead.

Unfortunately, I am unable to figure out how to avoid the ambiguity created by adding another input method for double. For the implementation below, I created a wrapper structure around an istream, and the wrapper class implements the input method. The input method determines negativity, then tries to extract a double. If that fails, it starts a parse.

Edit: Thanks to sehe for getting me to check for error conditions better.

struct double_istream {
    std::istream &in;

    double_istream (std::istream &i) : in(i) {}

    double_istream & parse_on_fail (double &x, bool neg);

    double_istream & operator >> (double &x) {
        bool neg = false;
        char c;
        if (!in.good()) return *this;
        while (isspace(c = in.peek())) in.get();
        if (c == '-') { neg = true; }
        in >> x;
        if (! in.fail()) return *this;
        return parse_on_fail(x, neg);
    }
};

The parsing routine was a little trickier to implement than I first thought it would be, but I wanted to avoid trying to putback an entire string.

double_istream &
double_istream::parse_on_fail (double &x, bool neg) {
    const char *exp[] = { "", "inf", "NaN" };
    const char *e = exp[0];
    int l = 0;
    char inf[4];
    char *c = inf;
    if (neg) *c++ = '-';
    in.clear();
    if (!(in >> *c).good()) return *this;
    switch (*c) {
    case 'i': e = exp[l=1]; break;
    case 'N': e = exp[l=2]; break;
    }
    while (*c == *e) {
        if ((e-exp[l]) == 2) break;
        ++e; if (!(in >> *++c).good()) break;
    }
    if (in.good() && *c == *e) {
        switch (l) {
        case 1: x = std::numeric_limits<double>::infinity(); break;
        case 2: x = std::numeric_limits<double>::quiet_NaN(); break;
        }
        if (neg) x = -x;
        return *this;
    } else if (!in.good()) {
        if (!in.fail()) return *this;
        in.clear(); --c;
    }
    do { in.putback(*c); } while (c-- != inf);
    in.setstate(std::ios_base::failbit);
    return *this;
}

One difference in behavior this routine will have over the the default double input is that the - character is not consumed if the input was, for example "-inp". On failure, "-inp" will still be in the stream for double_istream, but for a regular istream only "inp" will be left in the the stream.

std::istringstream iss("1.0 -NaN inf -inf NaN 1.2");
double_istream in(iss);
double u, v, w, x, y, z;
in >> u >> v >> w >> x >> y >> z;
std::cout << u << " " << v << " " << w << " "
          << x << " " << y << " " << z << std::endl;

The output of the above snippet on my system is:

1 nan inf -inf nan 1.2

Edit: Adding a "iomanip" like helper class. A double_imanip object will act like a toggle when it appears more than once in the >> chain.

struct double_imanip {
    mutable std::istream *in;
    const double_imanip & operator >> (double &x) const {
        double_istream(*in) >> x;
        return *this;
    }
    std::istream & operator >> (const double_imanip &) const {
        return *in;
    }
};

const double_imanip &
operator >> (std::istream &in, const double_imanip &dm) {
    dm.in = &in;
    return dm;
}

And then the following code to try it out:

std::istringstream iss("1.0 -NaN inf -inf NaN 1.2 inf");
double u, v, w, x, y, z, fail_double;
std::string fail_string;
iss >> double_imanip()
    >> u >> v >> w >> x >> y >> z
    >> double_imanip()
    >> fail_double;
std::cout << u << " " << v << " " << w << " "
          << x << " " << y << " " << z << std::endl;
if (iss.fail()) {
    iss.clear();
    iss >> fail_string;
    std::cout << fail_string << std::endl;
} else {
    std::cout << "TEST FAILED" << std::endl;
}

The output of the above is:

1 nan inf -inf nan 1.2
inf

Edit from Drise: I made a few edits to accept variations such as Inf and nan that wasn't originally included. I also made it into a compiled demonstration, which can be viewed at http://ideone.com/qIFVo.

Drise
  • 4,310
  • 5
  • 41
  • 66
jxh
  • 69,070
  • 8
  • 110
  • 193
  • What about `NaN` parsing? I know the question doesn't explicitly ask for it, but it is reasonable to include it due to the close relationship `NaN` has to this problem. – Drise Jul 10 '12 at 20:46
  • Very interesting edit. Could you show a (ideone) snippet of it in use? – Drise Jul 10 '12 at 22:48
  • @Drise: Snippet included in the answer. Regards – jxh Jul 10 '12 at 22:53
  • Ok nifty. Why not make it an iomanip/facet thing while you're at it? That way, you'll be able to mix and match with any iostreams classes – sehe Jul 10 '12 at 23:02
  • @sehe: The OP wanted to avoid a lot of modifications to existing code. Any `iomanip` I can think of now would involve injecting the manipulator before every input variable. Do you have a better way? Oh wait, I think I see how to do it. – jxh Jul 10 '12 at 23:10
  • @sehe: I could implement everything with `peek`/`get`, but I still have a day job... :-) – jxh Jul 10 '12 at 23:13
  • I noticed a bit late that parse_on_fail is only being called in known context (I read your code bottom up). In that case, you'd really have to check that the underlying stream is `.good()` before commencing parsing, I think – sehe Jul 10 '12 at 23:16
  • @sehe: Thanks, I changed the check against `good`, and only try to take corrective action if the `!good` means `fail`. – jxh Jul 11 '12 at 00:17
  • @user315052 Mmmm, I meant you'd need to check that `in.good()` inside `double_istream::operator>>(double&)` before even commencing any parsing – sehe Jul 11 '12 at 00:23
  • @user315052 I think it would also be interesting if you were to add an `iomanip` to your answer. Of course, it's not needed based on the question, but I think it would prove fairly useful. – Drise Jul 11 '12 at 00:37
4

Just read your variables into string and parse them. You can't put string into double variables and expect them to be outputted like a string, because if it worked, strings wouldn't be necessary.

Seomthing like:

string text;
double d;
while(cin >> text)
{
    if(text == "Inf")       //you could also add it with negative infinity
    {
         d = std::numeric_limits<double>::infinity();
    }
    else
    {
        d = atof(text.c_str());
    }
}
Blood
  • 4,126
  • 3
  • 27
  • 37
1

Although the question is quite old, I would like to contribute the solution that suited my purposes the most. If you consider using Boost, but Boost Spirit seems to be an overkill, you could try using the boost::math::nonfinite_num_put and boost::math::nonfinite_num_get locale facets 1 like this:

#include <boost/math/special_functions/nonfinite_num_facets.hpp>
#include <iostream>
#include <limits>
#include <sstream>

int main()
{
  std::locale default_locale;
  std::locale tmp_locale(default_locale,
      new boost::math::nonfinite_num_put<char>());
  std::locale upgraded_locale(default_locale,
      new boost::math::nonfinite_num_get<char>());

  double inf = std::numeric_limits<double>::infinity();

  std::stringstream out_s;
  out_s.imbue(upgraded_locale);
  out_s << inf;
  std::cout << out_s.str() << std::endl;
  std::stringstream in_s(out_s.str());
  in_s.imbue(upgraded_locale);
  double check_inf;
  in_s >> check_inf;
  std::cout << (inf == check_inf ? "nice" : "not nice") << std::endl;
  return 0;
}

Output:

inf
nice
0

You will have to write a custom extraction function, since your specific implementation clearly does not handle them correctly.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 9
    Remove the word "extraction" and this answer could be used to answer virtually every SO question ever asked... paraphrased: "You will have to do custom work because your implementation clearly does not work." -- not very helpful :( – Jesse Webb Jul 10 '12 at 19:40
  • @JesseWebb: The question was "why doesn't this work". The answer is "because your standard library implementation does not meet the spec". Additional information is very handy, but not required. – Mooing Duck Jul 10 '12 at 19:49
  • 2
    @MooingDuck "How can I get the functionality I seek?" was also part of the question. – Drise Jul 10 '12 at 19:50