jpo38
jpo38

Reputation: 21514

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...).

Upvotes: 9

Views: 525

Answers (5)

vitaut
vitaut

Reputation: 55605

In C++20 you'll be able to use std::format to do this:

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

This will print nan on all platforms that have quiet_NaN.

Disclaimer: I'm the author of C++20 std::format.

Upvotes: 0

user2249683
user2249683

Reputation:

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

Upvotes: 5

&#214;&#246; Tiib
&#214;&#246; Tiib

Reputation: 11014

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

Upvotes: 2

Sam Varshavchik
Sam Varshavchik

Reputation: 118445

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

#include <cmath>

// ...

double d;

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

Upvotes: 0

Marcus M&#252;ller
Marcus M&#252;ller

Reputation: 36402

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

Upvotes: 3

Related Questions