我想使用iostream和Visual C ++从文本文件中读取和写入NaN值。在写入NaN值时,我得到1.#QNAN
。但是,阅读它会输出1.0
。
float nan = std::numeric_limits<float>::quiet_NaN ();
std::ofstream os("output.txt");
os << nan ;
os.close();
输出为1.#QNAN
。
std::ifstream is("output.txt");
is >> nan ;
is.close();
nan
等于1.0
。
解决方案
最后,正如awoodland所建议的那样,我想出了这个解决方案。我选择“nan”作为NaN的字符串表示。两者都&lt;&lt;和&gt;&gt;运营商被覆盖。
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;
}
最小的工作示例:将有限浮点数和NaN写入字符串流然后回读。
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"
}
答案 0 :(得分:16)
将float
或double
值打印到std::ostream
时,将使用类模板std::num_put<>
(C ++03§22.2.2.2)。它将printf
打印的值格式化为%e
,%E,
%f
,%g
或%G
格式说明符,具体取决于在流的标志上(表58)。
同样,在输入float
或double
值时,它会像scanf
函数一样读取它,格式说明符为%g
(第22.2.2.1节)。 2/5)。
因此,下一个问题是scanf
无法正确解析1.#QNAN
的原因。 C89标准在其fprintf
和fscanf
函数的描述中未提及NaN。它确实表示浮点数的表示是未指定的,因此这是未指定的行为。
C99确实指明了这里的行为。对于fprintf
(C99§7.19.6.1/ 8):
指定表示无穷大的
double
参数在其中一种样式中转换[-]inf
或[-]infinity
- 哪种样式是实现定义的。一个 表示NaN的double
参数在其中一个样式中转换[-]nan
或[-]nan(n-char-sequence)
- 哪种风格和含义 任何 n-char-sequence ,都是实现定义的。 F转换说明符 生成INF
,INFINITY
或NAN
而不是inf
,infinity
或nan
, 分别 243)
fscanf
根据strtod(3)
解析数字(C99§7.19.6.2/ 12)。 strtod
解析如下(§7.20.1.3/ 3):
主题序列的预期形式是可选的加号或减号,然后是其中之一 以下内容:
- 非空的十进制数字序列,可选地包含小数点 字符,然后是6.4.4.2中定义的可选指数部分;
-0x
或0X
,然后是非空的十六进制数字序列,可选地包含一个 小数点字符,然后是6.4.4.2中定义的可选二进制指数部分;
-INF
或INFINITY
,忽略案例
-NAN
或NAN(n-char-sequenceopt)
,忽略NAN部分中的案例,其中:n-char-sequence: digit nondigit n-char-sequence digit n-char-sequence nondigit主题序列被定义为输入字符串的最长初始子序列, 从第一个非空白字符开始,这是预期的形式。主题 如果输入字符串不是预期的形式,则sequence不包含任何字符。
因此,在完成所有操作后,最终结果是您的C标准库不符合C99,因为根据上述内容1.#QNAN
不是fprintf
的有效输出。但是,众所周知,微软的C运行时不符合C99标准,而且据我所知,它并不打算很快成为合规用户。由于C89没有指定NaNs的行为,所以你运气不好。
您可以尝试切换到不同的编译器和C运行时(例如Cygwin + GCC),但对于如此小的指甲来说,这是一个非常大的锤子。如果你真的需要这种行为,我建议为浮点数编写一个包装类,它能够正确地格式化和解析NaN值。
答案 1 :(得分:7)
使用C ++ 03,您可以借助辅助类和您自己的运算符轻松解决该问题:
#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;
}
这非常适合我的平台上的NaN,但也说明了其中固有的可移植性问题 - 您的库似乎以不同的方式表示它,如果您想支持两者,这可能会使问题复杂化。 我用它测试了这个测试:
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;
}
您还可以将此语义更改为可能更清晰的内容:
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;
}
需要更改FloatNaNNHelper
:
struct FloatNaNHelper {
float& value;
explicit FloatNaNHelper(float& f) : value(f) { }
};
操作员:
std::istream& operator>>(std::istream& in, const FloatNaNHelper& f);