2016-02-08 13 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 выводить «нан», а не «1. # QNAN» .... нота, что VS2015 следует стандарт и выходы «nan», что в порядке. – 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' для этого, NaNs никогда не сравниваются ни с чем (даже сами). Это одна из их более интересных свойств. –

+1

@MarkRansom: Вы можете использовать 'isnan' – jpo38

+0

@ jpo38 Я это знаю. Я подталкивал к получению улучшенного ответа. –

0

Используйте isnan() для переносимости, если double является NaN.

#include <cmath> 

// ... 

double d; 

if (isnan(d)) 
    // ... 
+0

Я знаю, как проверить, является ли число 'nan', но, очевидно, для огромного проекта, где числа перенаправляются на выходы и файлы во многих местах, это будет боль, чтобы добавить тест повсюду. Я ищу глобальное решение (либо модификацию 'std :: locale', либо используемые атрибуты« ostream »). – jpo38

2
  • Выведите ваш YourNumPut от std::num_put.
  • Override то, что вам нужно, например: 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());
  • Установить его глобальным, наделить cout и cerr или где вам нужно: std::locale::global(yourLocale); std::cout.imbue(yourLocale); std::cerr.imbue(yourLocale);

  • Попробуй

  • ...
  • Прибыль;)
+0

Я пробовал это, но не мог понять, как отправить строку (например, «нан») в выходной поток в методе перегрузки 'do_put' ... не могли бы вы опубликовать компиляционный пример? – jpo38

+0

Просто std :: скопировать в параметр. –