是否有可能使用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
NaN
parsing也涉及到这个问题,尽pipe我没有给出它的例子。
我在接受的答案中添加了ideone的完整解决scheme。 它还包括“Inf”和“nan”的匹配,这些关键字可能来自其他程序(如MatLab)。
编辑:为了避免使用双重的包装结构,我把一个istream
封装在一个包装类中。
不幸的是,我无法弄清楚如何避免为double
添加另一个input方法而产生的歧义。 对于下面的实现,我围绕一个istream
创build了一个包装器结构,而包装器类实现了input方法。 input法确定负面,然后尝试提取一个双。 如果失败,则开始parsing。
编辑:感谢sehe让我更好地检查错误条件。
struct double_istream { std::istream ∈ 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; }
这个例程在默认的double
input上的行为的一个区别是,如果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 = ∈ 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)的相应部分似乎没有以相同的方式指定对infinity
, inf
和/或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);
在里面,你:
- 使用
operator>>
从stream中读取一个string - 尝试使用各种方法之一将string转换为双精度型。
std::stod
,boost::lexical_cast
等… - 如果转换成功,则设置double并返回stream。
- 如果转换失败,请使用“inf”或“INF”或其他来testingstring是否相等。
- 如果testing通过,则将double设置为infinity并返回stream,否则:
- 如果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()); } }
你将不得不编写一个自定义提取函数,因为你的具体实现显然不能正确处理它们。