用Visual c++编写的NaN ASCII I/O
NaN ASCII I/O with Visual C++
我想使用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
。
最后,根据woodland的建议,我想出了这个解决方案。我选择"nan"作为nan的字符串表示。& lt; & lt;和>>操作符被重写。
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写入stringstream,然后再读取。
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"
}
当将float
或double
的值打印到std::ostream
时,将使用类模板std::num_put<>
(c++ 03§22.2.2.2)。它按照%e
、%E,
、%f
、%g
或%G
格式说明符之一对printf
输出的值进行格式化,具体取决于流的标志(表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:数字数字n-char-sequence数字n-char-sequence数字之前主题序列定义为输入字符串的最长初始子序列,从第一个非空白字符开始,这是预期的形式。这个话题如果输入字符串不是期望的形式,则序列不包含任何字符。
因此,在考虑了所有这些之后,最终结果是您的C标准库不符合c99,因为根据上面的1.#QNAN
不是fprintf
的有效输出。但是,众所周知,微软的C运行时不兼容c99,而且据我所知,它也不打算很快兼容。由于C89没有指定这里关于nan的行为,您就不走运了。
您可以尝试切换到不同的编译器和C运行时(如Cygwin+GCC),但这对于这样一个小钉子来说是一个非常大的打击。如果您确实需要这种行为,我建议为浮点数编写一个包装器类,它能够正确格式化和解析NaN值。
使用c++ 03,您可以相当容易地通过helper类和您自己的操作符来解决这个问题:
#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);
- g++的分段错误(在NaN上使用to_string两次时)
- 输出是NaN,如何
- C++,在int数组中输入字符串或字符会输出0,而不是ascii或error
- 为什么我在输出端得到 nan?
- 提升反序列化对象具有 nan 或 -nan 值
- 为什么我的C++程序的程序集输出充满了 .ascii,没有汇编代码?
- 有没有办法通过使用十进制 ASCII 代码自动类型扣除来获取字符?
- 如何将字节数组元素替换为修改的十六进制 ASCII 符号?
- Is !NaN not a NaN?
- ascii 和 unicode 在处理级别有什么区别吗?
- NaN 上的宇宙飞船操作员
- 在C++中使用 ASCII 代码将输入从小写转换为大写
- 弄清楚如何在C++中将整数读入 ASCII
- C++ STL 排序会检查 NaN 吗?
- C++ 每次运行程序时我都会"nan"输出的问题
- 如何修复艺术ASCII,我点击一个字母就可以了,但输入一个阶段艺术出来了
- 使用指针将 ASCII 值添加到整数
- 用于 ASCII 的环绕式 C++
- RE2 不匹配非 ASCII 字符
- 用Visual c++编写的NaN ASCII I/O