2016-02-08 22 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参数。 –

相关问题