如何让所有平台编译器为 NaN 输出相同的字符串

How to have all platform compiler output the same string for NaN?

本文关键字:输出 NaN 字符串 编译器 平台      更新时间:2023-10-16

考虑以下代码片段:

#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"。

但是,我需要两者来产生相同的输出。最简单的方法是什么?我试图为double覆盖operator<<,但我觉得这不是正确的方法。用于NaN值的字符串是否可以在stream级别强制使用,或者更好的是,在全局级别强制(使用std::locale的东西?...从来没有用过...

我发现这个squaring_num_put例子。有趣的是,这是一种修改数字的方法,被重定向到输出。但是我很难尝试使其适应我的问题(无法do_putostream发送数字或硬编码的"NaN"字符串......

您可以使用流操纵器或修改基础区域设置:

机械手:

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

只需根据quiet_NaN值实现自己的检查,并基于该值进行打印。

  • 从 std::num_put 派生您的YourNumPut
  • 覆盖您需要的内容,例如: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);

  • 测试它

  • 利润;)

使用 isnan () 可移植地测试double是否为 NaN。

#include <cmath>
// ...
double d;
if (isnan(d))
   // ...

在 C++20 中,您将能够使用 std::format 来执行此操作:

std::cout << std::format("{}", std::numeric_limits<double>::quiet_NaN());

这将在所有具有quiet_NaN的平台上打印nan

免责声明:我是C++20 std::format的作者。