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

You might use a stream manipulator or modify the underlying locale:

Manipulator:

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

}

Locale:

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

Just implement your own checking against the quiet_NaN value, and print based on that.


  • Derive your YourNumPut from std::num_put.
  • Override what you need for example: virtual iter_type do_put( iter_type out, std::ios_base& str, char_type fill, double v ) const;
  • Create locale that uses it: std::locale yourLocale(std::locale(), new YourNumPut());
  • Set it global, imbue cout and cerr or where you need: std::locale::global(yourLocale); std::cout.imbue(yourLocale); std::cerr.imbue(yourLocale);

  • Test it

  • ...
  • Profit ;)

Tags:

C++

Ostream

Nan