2011-09-19 13 views
17

के साथ NaN ASCII I/O 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(); 

nan1.0 बराबर होती है।

समाधान

अंत में, के रूप में awoodland ने सुझाव दिया, मैं इस समाधान के साथ आए हैं। मैंने एक नैन के स्ट्रिंग प्रस्तुति के रूप में "नैन" चुना है। < < और >> ऑपरेटर ओवरराइड हैं।

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

एक न्यूनतम कामकाजी उदाहरण: एक सीमित फ्लोट और एक नाएन स्ट्रिंगस्ट्रीम में लिखा जाता है और फिर वापस पढ़ा जाता है।

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 (फ़्लोटिंग-पॉइंट प्रारूप में एन्कोडेड एक प्रतीकात्मक इकाई) के लिए वर्ण स्ट्रिंग प्रस्तुतियों को उत्पन्न करता है आईईईई स्टडी 754-19 85 का समर्थन करें, fscanf() फ़ंक्शन के परिवार उन्हें इनपुट के रूप में पहचानेंगे। 'जो भी इसके लायक है। –

+0

@Moo: यदि यह सच है, तो कम से कम हम अब जानते हैं कि iostreams 'fscanf' का उपयोग नहीं करते हैं :-) किसी भी मामले में जो भरोसेमंद चीज पर भरोसा करना होगा, क्योंकि NaN का पाठ प्रस्तुतिकरण कंपाइलर से भिन्न होता है कंपाइलर, और शायद राज्य से राज्य तक भी। शायद * ना * पढ़ने के लिए यह संभव नहीं है। (इसके बारे में सोचने के लिए यहां तक ​​कि इसके लिए एक शाब्दिक भी नहीं है।) –

उत्तर

7
के साथ सी ++ 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); 
16

एक std::ostream, वर्ग टेम्पलेट std::num_put<> प्रयोग किया जाता है (सी ++ 03 §22.2.2.2) के लिए एक float या double मूल्य मुद्रण है। यह द्वारा , %E,%f, %g, या %G प्रारूप विनिर्देशों के साथ स्ट्रीम के झंडे (तालिका 58) के आधार पर मुद्रित मान को प्रारूपित करता है।

इसी तरह, जब एक float या double मूल्य inputting, यह के रूप में %g के एक फॉर्मेट स्पेसिफायर (§22.2.2.1.2/5) के साथ scanf समारोह के साथ करता है, तो पढ़ता है।

तो, अगला प्रश्न यह है कि scanf उचित रूप से 1.#QNAN का विश्लेषण नहीं करता है। C89 मानक fprintf और fscanf फ़ंक्शंस दोनों के विवरणों में NaN का उल्लेख नहीं करता है। यह कहता है कि फ़्लोटिंग-पॉइंट नंबरों का प्रतिनिधित्व अनिर्दिष्ट है, इसलिए यह अनिर्दिष्ट व्यवहार में पड़ता है।

सी 99, दूसरी ओर, यहां व्यवहार निर्दिष्ट करता है। fprintf (C99 §7.19.6.1/8) के लिए:

एक double तर्क एक अनन्तता का प्रतिनिधित्व शैलियों [-]inf या [-]infinity में से एक में बदल जाती है - जो शैली कार्यान्वयन परिभाषित किया गया है। एक double तर्क एक NaN का प्रतिनिधित्व शैलियों [-]nan या [-]nan(n-char-sequence) में से एक में बदल जाती है - जो शैली, और किसी भी एन-चार-अनुक्रम के अर्थ, कार्यान्वयन परिभाषित किया गया है। एफ रूपांतरण विनिर्देशक INF, INFINITY, या NAN बजाय inf, infinity, या nan, क्रमशः पैदा करता है। 243)

fscanfstrtod(3) (C99 §7.19.6.2/12) के अनुसार नंबर पार्स करने निर्दिष्ट किया जाता है।

विषय अनुक्रम की उम्मीद प्रपत्र एक वैकल्पिक प्लस या ऋण चिह्न, में से एक निम्नलिखित है तो: strtod के रूप में इस प्रकार है (§7.20.1.3/3) को पार्स करता
- दशमलव अंकों के एक अरिक्त अनुक्रम वैकल्पिक रूप से दशमलव-बिंदु वर्ण, फिर एक वैकल्पिक एक्सपोनेंट भाग 6.4.4.2 में परिभाषित किया गया है;
- 0x या 0X, फिर हेक्साडेसिमल अंकों का एक nonempty अनुक्रम वैकल्पिक रूप से दशमलव-बिंदु वर्ण, फिर एक वैकल्पिक बाइनरी एक्सपोनेंट भाग 6.4.4.2 में परिभाषित किया गया है;
- INF या INFINITY, अनदेखी मामले
- NAN या NAN(n-char-sequenceopt), नान हिस्सा है, जहां के मामले में अनदेखी:

 
n-char-sequence: 
    digit 
    nondigit 
    n-char-sequence digit 
    n-char-sequence nondigit 
विषय अनुक्रम इनपुट स्ट्रिंग की सबसे लंबी प्रारंभिक परिणाम को परिभाषित किया गया है, पहले अश्वेत के साथ शुरू स्पेस चरित्र, जो अपेक्षित रूप में है। यदि इनपुट स्ट्रिंग अपेक्षित रूप से नहीं है तो विषय अनुक्रम में कोई वर्ण नहीं है।


तो, वह सब में लेने के बाद, अंतिम परिणाम है कि आपके सी मानक पुस्तकालय, C99 अनुरूप नहीं है, क्योंकि 1.#QNAN ऊपर के अनुसार fprintf का एक मान्य उत्पादन नहीं है। लेकिन, यह अच्छी तरह से ज्ञात है कि माइक्रोसॉफ्ट का सी रनटाइम सी 99-अनुरूप नहीं है, और जहां तक ​​मुझे पता है, यह जल्द ही किसी भी समय अनुपालन करने की योजना नहीं बना रहा है। चूंकि सी 8 9 नाएन के संबंध में व्यवहार को निर्दिष्ट नहीं करता है, इसलिए आप भाग्य से बाहर हैं।

आप एक अलग कंपाइलर और सी रनटाइम (जैसे सिगविन + जीसीसी) पर स्विच करने का प्रयास कर सकते हैं, लेकिन यह इतनी छोटी नाखून के लिए एक बहुत बड़ा हथौड़ा है। यदि आपको वास्तव में इस व्यवहार की आवश्यकता है, तो मैं फ्लोट्स के लिए एक रैपर क्लास लिखने की अनुशंसा करता हूं जो सही रूप से स्वरूपण और नाइन मानों को पार्स करने में सक्षम है।

+0

बहुत अच्छा जवाब! –

+0

अच्छा जवाब, लेकिन एक अलग कंपाइलर पर स्विच करना मेरे लिए एक विकल्प नहीं है। – Mourad