2016-02-08 58 views
8

考慮以下代碼片段:如何讓所有平臺編譯器輸出相同的字符串爲NaN?

#include <iostream> 
#include <string> 
#include <limits> 

int main() 
{ 
    std::cout << std::numeric_limits<double>::quiet_NaN(); 
} 

當與Visual Studio 2010的編譯,輸出爲1.#QNAN。當用g ++編譯時,輸出是nan。請注意,Visual Studio 2015輸出「nan」。

但是,我需要兩個產生相同的輸出。什麼是最簡單的方法來做到這一點?我試圖覆蓋operator<<double,但我覺得這不是正確的做法。可以使用字符串NaN值在stream級別或更好,在全局級別(使用std::locale的東西?......從未使用過......)。

我發現這個squaring_num_put的例子。有趣的,因爲它是一種修改數字的方式被重定向到輸出。但我很難適應它到我的問題(不能讓do_put發送一個數字或硬編碼的「NaN」字符串到ostream ...)。

+11

*我既需要產生相同的輸出*。聽起來像XY問題... –

+2

數字輸出最終調用['std :: num_put :: do_put'](http://en.cppreference.com/w/cpp/locale/num_put/put)使用['std :: printf'](http://en.cppreference.com/w/cpp/io/c/fprintf)格式化輸出。和['std :: printf'](http://en.cppreference.com/w/cpp/io/c/fprintf)參考頁面一樣,NaN的輸出應該是'nan'或'nan(char_sequence )'。 (哪一個是實現定義的)這意味着Visual Studio是錯誤的。這也意味着,即使對於兼容的編譯器和庫,你有兩個替代輸出來處理 –

+0

所以問題可能是如何強制VS2010輸出「nan」而不是「1.#QNAN」....不是VS2015遵循標準和輸出「南」,這是好的。 – jpo38

回答

5

您可以使用一個流處理器或修改底層區域:

機械手:

#include <cmath> 
#include <ostream> 

template <typename T> 
struct FloatFormat 
{ 
    const T value; 

    FloatFormat(const T& value) 
    : value(value) 
    {} 

    void write(std::ostream& stream) const { 
     if(std::isnan(value)) 
      stream << "Not a Number"; 
     else 
      stream << value; 
    } 
}; 

template <typename T> 
inline FloatFormat<T> float_format(const T& value) { 
    return FloatFormat<T>(value); 
} 

template <typename T> 
inline std::ostream& operator << (std::ostream& stream, const FloatFormat<T>& value) { 
    value.write(stream); 
    return stream; 
} 

int main() { 
    std::cout << float_format(std::numeric_limits<double>::quiet_NaN()) << '\n'; 

} 

區域設置:

#include <cmath> 
#include <locale> 
#include <ostream> 

template<typename Iterator = std::ostreambuf_iterator<char>> 
class NumPut : public std::num_put<char, Iterator> 
{ 
    private: 
    using base_type = std::num_put<char, Iterator>; 

    public: 
    using char_type = typename base_type::char_type; 
    using iter_type = typename base_type::iter_type; 

    NumPut(std::size_t refs = 0) 
    : base_type(refs) 
    {} 

    protected: 
    virtual iter_type do_put(iter_type out, std::ios_base& str, char_type fill, double v) const override { 
     if(std::isnan(v)) 
      out = std::copy(std::begin(NotANumber), std::end(NotANumber), out); 
     else 
      out = base_type::do_put(out, str, fill, v); 
     return out; 
    } 

    virtual iter_type do_put(iter_type out, std::ios_base& str, char_type fill, long double v) const override { 
     if(std::isnan(v)) 
      out = std::copy(std::begin(NotANumber), std::end(NotANumber), out); 
     else 
      out = base_type::do_put(out, str, fill, v); 
     return out; 
    } 

    private: 
    static const char NotANumber[]; 
}; 

template<typename Iterator> 
const char NumPut<Iterator>::NotANumber[] = "Not a Number"; 

#include <iostream> 
#include <limits> 

int main() { 
    #if 1 
    { 
     const std::size_t NoDestroy = 1; 
     NumPut<> num_put(NoDestroy); 
     std::locale locale(std::cout.getloc(), &num_put); 
     std::locale restore_locale = std::cin.getloc(); 
     std::cout.imbue(locale); 
     std::cout << std::numeric_limits<double>::quiet_NaN() << '\n'; 
     // The num_put facet is going out of scope: 
     std::cout.imbue(restore_locale); 
    } 
    #else 
    { 
     // Alternitvely use a reference counted facet and pass the ownership to the locales: 
     auto num_put = new NumPut<>(); 
     std::locale locale(std::cout.getloc(), num_put); 
     std::cout.imbue(locale); 
     std::cout << std::numeric_limits<double>::quiet_NaN() << '\n'; 
    } 
    #endif 
    std::cout << std::numeric_limits<double>::quiet_NaN() << '\n'; 
} 
+0

謝謝!這正是我所期待的! – jpo38

3

只需對quiet_NaN值執行自己的檢查,然後根據該值進行打印。

+1

但是,你不能僅僅使用'== quiet_NaN',NaN永遠不會與任何東西(甚至它們自己)相等。這是他們更有趣的屬性之一。 –

+1

@MarkRansom:你可以使用'isnan' – jpo38

+0

@ jpo38我知道。我正在推動獲得改進的答案。 –

0

使用isnan()來移植測試double是否是NaN。

#include <cmath> 

// ... 

double d; 

if (isnan(d)) 
    // ... 
+0

我知道如何測試一個數字是否爲'nan',但顯然,對於在很多地方將數字重定向到輸出和文件的大型項目來說,在任何地方添加測試都會很痛苦。我正在尋找一個全局解決方案(修改'std :: locale'或當前使用的'ostream'屬性)。 – jpo38

2
  • 派生您YourNumPutstd::num_put
  • 覆蓋你需要,例如什麼:virtual iter_type do_put(iter_type out, std::ios_base& str, char_type fill, double v) const;
  • 創建一個使用它的語言環境:std::locale yourLocale(std::locale(), new YourNumPut());
  • 設置它的全球,灌輸coutcerr或者你需要:std::locale::global(yourLocale); std::cout.imbue(yourLocale); std::cerr.imbue(yourLocale);

  • 測試它

  • ...
  • 利潤;)
+0

我試過了,但無法弄清楚如何發送一個字符串(比如「nan」)到'do_put'重載方法內的輸出流....請問您可以發佈一個編譯示例嗎? – jpo38

+0

只需std :: copy到out參數。 –

相關問題