2016-02-08 15 views
8

Hãy xem xét đoạn mã này:Làm thế nào để có tất cả đầu ra trình biên dịch nền tảng cùng một chuỗi cho NaN?

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

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

Khi biên soạn với Visual Studio 2010, sản lượng là 1.#QNAN. Khi được biên dịch bằng g ++, đầu ra là nan. Lưu ý rằng Visual Studio 2015 xuất ra "nan".

Tuy nhiên, tôi cần cả hai để tạo ra cùng một đầu ra. Cách đơn giản nhất để làm điều đó là gì? Tôi đã cố gắng ghi đè operator<< cho double nhưng tôi cảm thấy đó không phải là cách phù hợp để thực hiện. Chuỗi có thể được sử dụng cho giá trị NaN bị buộc ở mức stream hoặc tốt hơn ở cấp toàn cầu (sử dụng công cụ std::locale? ... không bao giờ được sử dụng ...).

Tôi đã tìm thấy ví dụ này squaring_num_put. Thú vị vì đó là một cách để sửa đổi một số được chuyển hướng đến đầu ra. Nhưng tôi đang gặp khó khăn trong việc cố gắng thích ứng với vấn đề của mình (Không thể làm cho do_put gửi một số hoặc một chuỗi "NaN" được mã hóa cứng tới số ostream ...).

+11

* Tôi cần cả hai để tạo đầu ra tương tự *. Âm thanh như một vấn đề XY với tôi ... –

+2

Xuất các số cuối cùng gọi ['std :: num_put :: do_put'] (http://en.cppreference.com/w/cpp/locale/num_put/put) về cơ bản sử dụng ['std :: printf'] (http://en.cppreference.com/w/cpp/io/c/fprintf) để định dạng đầu ra. Và khi các trang tham chiếu ['std :: printf'] (http://en.cppreference.com/w/cpp/io/c/fprintf) nói, đầu ra cho NaN phải là' nan' hoặc 'nan (char_sequence)) '. (mà một là thực hiện xác định) Điều này có nghĩa rằng Visual Studio là sai. Nó cũng có nghĩa là ngay cả đối với các trình biên dịch và thư viện tương thích, bạn có hai đầu ra thay thế để xử lý –

+0

Vì vậy, câu hỏi có thể là cách buộc VS2010 xuất "nan" chứ không phải "1. # QNAN" .... tiêu chuẩn và đầu ra "nan", đó là OK. – jpo38

Trả lời

5

Bạn có thể sử dụng một thao túng dòng hoặc sửa đổi các miền địa phương cơ bản:

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

Cảm ơn! Đó chính xác là những gì tôi đang tìm kiếm! – jpo38

3

Chỉ cần thực hiện kiểm tra của riêng bạn với giá trị quiet_NaN và in dựa trên điều đó.

+1

Nhưng bạn không thể chỉ sử dụng '== quiet_NaN' cho điều đó, NaN không bao giờ so sánh bằng bất kỳ thứ gì (ngay cả chính chúng). Đó là một trong những đặc tính thú vị của họ. –

+1

@MarkRansom: Bạn có thể sử dụng 'isnan' – jpo38

+0

@ jpo38 Tôi biết điều đó. Tôi đã thúc đẩy để có được một câu trả lời được cải thiện. –

0

Sử dụng isnan() để kiểm tra một cách hợp lý nếu double là một NaN.

#include <cmath> 

// ... 

double d; 

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

Tôi biết làm thế nào để kiểm tra nếu một số là 'nan', nhưng, rõ ràng, đối với dự án lớn, nơi các con số đang chuyển hướng đến đầu ra và tệp ở nhiều nơi, sẽ là một nỗi đau để thêm thử nghiệm ở khắp mọi nơi. Tôi đang tìm một giải pháp toàn cầu (hoặc sửa đổi 'std :: locale' hoặc hiện đang sử dụng thuộc tính' ostream'). – jpo38

2
  • Rút ra YourNumPut của bạn từ std::num_put.
  • Override những gì bạn cần ví dụ: virtual iter_type do_put(iter_type out, std::ios_base& str, char_type fill, double v) const;
  • Tạo locale sử dụng nó: std::locale yourLocale(std::locale(), new YourNumPut());
  • Set nó toàn cầu, thấm nhuần coutcerr hoặc nơi bạn cần: std::locale::global(yourLocale); std::cout.imbue(yourLocale); std::cerr.imbue(yourLocale);

  • thử nghiệm nó

  • ...
  • Lợi nhuận;)
+0

Tôi đã thử điều đó, nhưng không thể tìm ra cách gửi một chuỗi (như "nan") tới luồng đầu ra trong phương thức nạp chồng 'do_put' .... bạn có thể viết một ví dụ biên dịch không? – jpo38

+0

Chỉ cần std :: sao chép tham số. –

Các vấn đề liên quan