2011-09-19 3 views
17

iostreamとVisual C++を使用して、テキストファイルのNaN値を読み書きする必要があります。 NaN値を書くとき、私は1.#QNANを得ます。しかし、それを読み取って、1.0を出力します。Visual C++を使用したNaN ASCII I/O

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

nan1.0に等しいです。 awoodlandにより示唆されるように

ソリューション

は最後に、私はこのソリューションを作ってみました。私はNaNの文字列表現として "nan"を選択しました。 < <と>>演算子は両方ともオーバーライドされます。

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がストリングストリームに書き込まれ、次に読み戻されます。 std::ostreamfloat又はdouble値を印刷する場合

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" 
} 
+3

質問は、「NaNでフォーマットされたI/Oを実行する方法」です。私は考えます。良い質問。 –

+0

http://pubs.opengroup.org/onlinepubs/007904975/functions/scanf.htmlは、関数のfprintf()ファミリが無限大とNaN(浮動小数点形式でエンコードされた記号エンティティ)の文字列表現を生成する場合、 IEEE Std 754-1985をサポートしていれば、fscanf()関数ファミリは、その値が何であれ、それらを入力として認識します。 –

+0

@Moo:もしそれが本当であれば、少なくとも私たちはiostreamが 'fscanf'を使わないことを知っています:-)どんな場合でも、NaNのテキスト表現はコンパイラからコンパイラ、そしておそらく州から州にも及んでいます。たぶん* NaNを読むことができないかもしれません。 –

答えて

7

あなたはかなり簡単にヘルパークラスの援助とあなた自身のオペレータとの問題を回避することができます。また、これは非常に賢明に私のプラットフォーム上でのNaNのために動作しますが、

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

が示しますあなたのライブラリはそれを別のものとして表現しているように見えますが、両方をサポートしたい場合は、問題をいくらか複雑にする可能性があります。

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

、クラステンプレートstd::num_put<>は、(C++ 03§22.2.2.2)が使用されます。ストリームのフラグ(表58)に応じて、printfのように値をフォーマットし、%e,%E,%f%g、または%Gのいずれかのフォーマット指定子でフォーマットします。 float又はdouble値を入力するとき

は同様に、それは%gの形式指定子(§22.2.2.1.2/ 5)とscanf機能を有するかのようにそれを読み出します。

したがって、次の質問はscanfが正しく1.#QNANを解析しない理由です。 C90標準では、fprintffscanfの両方の機能の説明でNaNについて言及していません。浮動小数点数の表現は指定されていないので、これは不特定の動作になります。

C99は、ここでの動作を指定しています。 fprintfため(C99§7.19.6.1/ 8):無限を表す

double引数をスタイル [-]inf又は[-]infinityのいずれかに変換される - 実装定義されたスタイル。 NaNを表す double引数をスタイル [-]nan又は[-]nan(n-char-sequence)のいずれかに変換される - スタイル、 任意N-チャー配列の意味は、実装定義です。 F変換指定 INFは、INFINITY、又はNAN代わりにinfinfinity、又はnan、 それぞれを生成します。 243)

fscanf

strtod(3)(C99§7.19.6.2/ 12)に従って番号を解析するために指定されています。次のようにstrtodは(§7.20.1.3/ 3)構文解析:対象配列の

期待形態は、オプションのプラスまたはマイナス記号であり、次いで の一つは次
- 桁の空でない配列オプションで小数点を含む 文字、次に6.4.4.2で定義されているオプションの指数部、
- a 0xまたは0Xの場合、任意に の小数点文字を含む16進数の空でないシーケンス、次に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 
それは予想される形式のスペース文字です。入力文字列が予想される形式でない場合、件名 には文字は含まれません。


だから、すべてのことをで取った後、最終的な結果は1.#QNANは、上記によるfprintfの有効な出力ではありませんので、あなたのC標準ライブラリは、C99に準拠していないということです。しかし、マイクロソフトのCランタイムはC99に準拠していないことがよく知られており、私が知っている限り、いつでも準拠する予定はない。 C89ではNaNに関してここでの動作を指定していないので、あなたは不運です。

異なるコンパイラとCランタイム(Cygwin + GCCなど)に切り替えてみることもできますが、それはそのような小さな爪にとって非常に大きなハンマーです。この動作が本当に必要な場合は、浮動小数点数のラッパークラスを作成して、NaN値を正しく書式設定して解析できることをお勧めします。 C++ 03で

+0

非常に良い答え! –

+0

良い答えですが、別のコンパイラに切り替えることは私の選択肢ではありません。 – Mourad

関連する問題