jpo38 February 2016

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

Consider this code snippet:

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

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

When compiled with Visual Studio 2010, output is 1.#QNAN. When compiled with g++, output is nan. Note that Visual Studio 2015 outputs "nan".

However, I need both to produce the same output. What's the most simple way to do that? I tried to override operator<< for double but I feel like that's not the right way to do. Can string to be used for NaN value be forced at stream level, or better, at global level (using std::locale stuff?...never used that...).

I found this squaring_num_put example. Interesting because it's a way to modify a number is redirected to the output. But I'm having a hard time trying to adapt it to my problem (Could not make do_put send either a number or a hard coded "NaN" string to the ostream...).

Answers


Marcus Müller February 2016

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


Sam Varshavchik February 2016

Use isnan() to portably test if a double is a NaN.

#include <cmath>

// ...

double d;

if (isnan(d))
   // ...


Öö Tiib February 2016

  • 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 ;)


Dieter Lücking February 2016

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:: 

Post Status

Asked in February 2016
Viewed 1,887 times
Voted 14
Answered 4 times

Search




Leave an answer