2016-02-08 9 views
8

Rozważmy następujący fragment kodu:Jak uzyskać wszystkie wyniki kompilatora platformy dla tego samego ciągu dla NaN?

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

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

Kiedy skompilowany z Visual Studio 2010, wyjście jest 1.#QNAN. Gdy skompilowane z g ++, wyjście to nan. Zwróć uwagę, że Visual Studio 2015 wyświetla "nan".

Potrzebuję jednak obu tych samych wyników. Jaki jest najprostszy sposób to zrobić? Próbowałem zastąpić operator<< dla double, ale wydaje mi się, że nie jest to właściwy sposób. Czy ciąg może być użyty dla wartości NaN wartość być wymuszona na poziomie stream, lub lepiej, na poziomie globalnym (używając std::locale rzeczy? ... nigdy nie używane, że ...).

Znalazłem ten przykład squaring_num_put. Interesujące, ponieważ jest to sposób modyfikowania liczby, jest przekierowywany na wyjście. Ale ciężko mi próbować dostosować go do mojego problemu (nie można ustawić, aby do_put wysyłał albo numer, albo zakodowany ciąg "NaN" do ostream ...).

+11

* Muszę zarówno do wytworzenia tej samej wyjście *. Brzmi jak problem XY do mnie ... –

+2

Wypisywanie liczb ostatecznie wywołuje ['std :: num_put :: do_put'] (http://en.cppreference.com/w/cpp/locale/num_put/put), które zasadniczo używa ['std :: printf'] (http://en.cppreference.com/w/cpp/io/c/fprintf) do formatowania danych wyjściowych. I jak mówi się na stronach ['std :: printf'] (http://en.cppreference.com/w/cpp/io/c/fprintf), wyjściem dla NaN powinno być' nan' lub 'nan (char_sequence) '. (która to implementacja jest zdefiniowana) Oznacza to, że Visual Studio jest źle. Oznacza to również, że nawet dla zgodnych kompilatorów i bibliotek masz dwa alternatywne wyjścia do obsługi –

+0

. Zatem może być pytanie, jak zmusić VS2010 do wyprowadzenia "nan" zamiast "1. # QNAN" .... nie, że VS2015 podąża za standard i wyjścia "nan", co jest w porządku. – jpo38

Odpowiedz

5

Można używać strumienia manipulator lub zmodyfikować podstawową regionalne:

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

Dzięki! Właśnie tego szukałem! – jpo38

3

Wystarczy zaimplementować własne sprawdzenie względem wartości quiet_NaN i wydrukować na tej podstawie.

+1

Ale nie można po prostu użyć '== quiet_NaN' do tego, NaNs nigdy nie porównywać do niczego (nawet siebie). To jedna z ich bardziej interesujących właściwości. –

+1

@MarkRansom: Możesz użyć 'isnan' – jpo38

+0

@ jpo38 Wiem o tym. Szukałem lepszej odpowiedzi. –

0

Użyj isnan(), aby testować przenośnie, czy double jest NaN.

#include <cmath> 

// ... 

double d; 

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

Wiem, jak przetestować, czy liczba to 'nan', ale, oczywiście, w przypadku ogromnego projektu, w którym liczby są przekierowywane na wyjścia i pliki w wielu miejscach, to będzie ból dodawać test wszędzie. Szukam globalnego rozwiązania (albo modyfikując 'std :: locale' lub obecnie używane atrybuty' ostream'). – jpo38

2
  • Wyprowadź swoją YourNumPut z std::num_put.
  • Zastąp co trzeba na przykład: virtual iter_type do_put(iter_type out, std::ios_base& str, char_type fill, double v) const;
  • Tworzenie locale, który używa go: std::locale yourLocale(std::locale(), new YourNumPut());
  • Set to globalny nasycić cout i cerr lub gdzie trzeba: std::locale::global(yourLocale); std::cout.imbue(yourLocale); std::cerr.imbue(yourLocale);

  • testowy to

  • ...
  • Zysk;)
+0

Próbowałem, ale nie mogłem dowiedzieć się, jak wysłać ciąg znaków (np. "Nan") do strumienia wyjściowego w metodzie przeciążania 'do_put' .... czy mógłbyś napisać kompilujący przykład? – jpo38

+0

Po prostu std :: copy to out parameter. –

Powiązane problemy