是否有可能使用inputstream读取无穷大或NaN值?

我有一些input被input文件stream(例如)读取:

-365.269511 -0.356123 -Inf 0.000000

当我使用std::ifstream mystream; 从文件中读取一些

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

(假设mystream已经打开,文件有效),

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

mystream处于失败状态。 我期望

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

输出

-365.269511 -0.356123 -1 -1 。 我希望它输出-365.269511 -0.356123 -Inf 0

这组数据是使用C ++stream输出的。 为什么我不能做相反的过程(在我的输出中读取)? 我如何获得我所寻求的function?

从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

输出:

 inf 0:inf 

另请参阅: http : //ideone.com/jVvei

NaNparsing也涉及到这个问题,尽pipe我没有给出它的例子。

我在接受的答案中添加了ideone的完整解决scheme。 它还包括“Inf”和“nan”的匹配,这些关键字可能来自其他程序(如MatLab)。

编辑:为了避免使用双重的包装结构,我把一个istream封装在一个包装类中。

不幸的是,我无法弄清楚如何避免为double添加另一个input方法而产生的歧义。 对于下面的实现,我围绕一个istream创build了一个包装器结构,而包装器类实现了input方法。 input法确定负面,然后尝试提取一个双。 如果失败,则开始parsing。

编辑:感谢sehe让我更好地检查错误条件。

 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); } }; 

parsing例程比我第一次想到的要稍微复杂一点,但是我想避免试图putback整个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; } 

这个例程在默认的doubleinput上的行为的一个区别是,如果input是"-inp" ,那么不会消耗-字符。 失败时, "-inp"仍然在double_istream的stream中,但是对于一个常规的istream只有"inp"会留在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; 

上面的代码在我的系统上的输出是:

 1 nan inf -inf nan 1.2 

编辑:添加像助手类“iomanip”。 当一个double_imanip对象在>>链中出现多次时,就会像切换一样。

 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; } 

然后下面的代码来试试看:

 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; } 

以上的输出是:

 1 nan inf -inf nan 1.2 inf 

来自Drise的编辑:我做了一些编辑,以接受最初并未包含的Inf和nan等变体。 我也把它编入了一个编译的演示,可以在http://ideone.com/qIFVo上查看。;

更新提供了一个简单的testing案例,它显示了升压精神能够处理在这个领域的所有品种的特殊价值。 见下文: 提升精神(FTW)

标准

C99标准的7.19.6.1/7.19.6.2节中唯一可以find的这方面的规范性信息。

不幸的是,最新的C ++标准文档(n3337.pdf)的相应部分似乎没有以相同的方式指定对infinityinf和/或NaN支持。 (也许我错过了一个引用C99 / C11规范的脚注?)

图书馆的执行者

在2000年,Apache libstdcxx收到一个错误报告,说明

num_get<> facet的do_get()成员未能考虑到特殊的string[-]inf[inity][-]nan 。 遇到此类string时,此方面会报告错误。 请参阅C99的7.19.6.1和7.19.6.2以获取允许的string列表。

然而,随后的讨论产生了(至less使用命名locale ),实现parsing特殊值实际上是非法的:

查找表中的字符是“0123456789abcdefABCDEF + – ”。 图书馆问题221会将其修改为“0123456789abcdefxABCDEFX + – ”。 查找表中不存在“N”,因此num_get <> :: do_get()的第2阶段不允许读取字符序列“NaN”。

其他资源

securecoding.cert.org明确指出,为避免parsing无穷大NaN, 需要使用以下“合规代码”。 这意味着,一些实现实际上支持 – 假设作者曾经testing过发布的代码。

 #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; } 

提升精神(FTW)

以下简单的示例具有所需的输出:

 #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'; } 

输出:

 3.14 -inf inf nan -nan nan 42 

摘要/ TL; DR

我倾向于说C99指定了* printf / * scanf的行为来包含无穷大NaN 。 C + + 11,遗憾的是似乎没有指定它(或甚至禁止它,在存在命名区域设置)。

用这样的签名写一个函数:

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

在里面,你:

  1. 使用operator>>从stream中读取一个string
  2. 尝试使用各种方法之一将string转换为双精度型。 std::stodboost::lexical_cast等…
  3. 如果转换成功,则设置double并返回stream。
  4. 如果转换失败,请使用“inf”或“INF”或其他来testingstring是否相等。
  5. 如果testing通过,则将double设置为infinity并返回stream,否则:
  6. 如果testing失败,请设置stream上的失败位并将其返回。

只要将你的variables读入string并parsing它们。 你不能把string放到双精度variables中,希望它们像string一样输出,因为如果它起作用的话,string就不是必需的了。

像是:

 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()); } } 

你将不得不编写一个自定义提取函数,因为你的具体实现显然不能正确处理它们。