| #ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP |
| #define BOOST_MATH_NONFINITE_NUM_FACETS_HPP |
| |
| // Copyright (c) 2006 Johan Rade |
| // Copyright 2011 Paul A. Bristow (comments) |
| |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE_1_0.txt |
| // or copy at http://www.boost.org/LICENSE_1_0.txt) |
| |
| /* |
| \file |
| |
| \brief non_finite_num facets for C99 standard output of infinity and NaN. |
| |
| \details See fuller documentation at Boost.Math Facets |
| for Floating-Point Infinities and NaNs. |
| */ |
| |
| #include <cstring> |
| #include <ios> |
| #include <limits> |
| #include <locale> |
| |
| #include <boost/version.hpp> |
| |
| #include <boost/math/special_functions/fpclassify.hpp> |
| #include <boost/math/special_functions/sign.hpp> |
| |
| #ifdef _MSC_VER |
| # pragma warning(push) |
| # pragma warning(disable : 4127) // conditional expression is constant. |
| # pragma warning(disable : 4706) // assignment within conditional expression. |
| # pragma warning(disable : 4224) // formal parameter 'version' was previously defined as a type. |
| #endif |
| |
| namespace boost { |
| namespace math { |
| |
| // flags (enums can be ORed together) ----------------------------------- |
| |
| const int legacy = 0x1; //!< get facet will recognize most string representations of infinity and NaN. |
| const int signed_zero = 0x2; //!< put facet will distinguish between positive and negative zero. |
| const int trap_infinity = 0x4; /*!< put facet will throw an exception of type std::ios_base::failure |
| when an attempt is made to format positive or negative infinity. |
| get will set the fail bit of the stream when an attempt is made |
| to parse a string that represents positive or negative sign infinity. |
| */ |
| const int trap_nan = 0x8; /*!< put facet will throw an exception of type std::ios_base::failure |
| when an attempt is made to format positive or negative NaN. |
| get will set the fail bit of the stream when an attempt is made |
| to parse a string that represents positive or negative sign infinity. |
| */ |
| |
| // class nonfinite_num_put ----------------------------------------------------- |
| |
| template< |
| class CharType, |
| class OutputIterator = std::ostreambuf_iterator<CharType> |
| > |
| class nonfinite_num_put : public std::num_put<CharType, OutputIterator> |
| { |
| public: |
| explicit nonfinite_num_put(int flags = 0) : flags_(flags) {} |
| |
| protected: |
| virtual OutputIterator do_put( |
| OutputIterator it, std::ios_base& iosb, |
| CharType fill, double val) const |
| { |
| put_and_reset_width(it, iosb, fill, val); |
| return it; |
| } |
| |
| virtual OutputIterator do_put( |
| OutputIterator it, std::ios_base& iosb, |
| CharType fill, long double val) const |
| { |
| put_and_reset_width(it, iosb, fill, val); |
| return it; |
| } |
| |
| private: |
| template<class ValType> void put_and_reset_width( |
| OutputIterator& it, std::ios_base& iosb, |
| CharType fill, ValType val) const |
| { |
| put_impl(it, iosb, fill, val); |
| iosb.width(0); |
| } |
| |
| template<class ValType> void put_impl( |
| OutputIterator& it, std::ios_base& iosb, |
| CharType fill, ValType val) const |
| { |
| switch((boost::math::fpclassify)(val)) { |
| |
| case FP_INFINITE: |
| if(flags_ & trap_infinity) |
| throw std::ios_base::failure("Infinity"); |
| else if((boost::math::signbit)(val)) |
| put_num_and_fill(it, iosb, "-", "inf", fill); |
| else if(iosb.flags() & std::ios_base::showpos) |
| put_num_and_fill(it, iosb, "+", "inf", fill); |
| else |
| put_num_and_fill(it, iosb, "", "inf", fill); |
| break; |
| |
| case FP_NAN: |
| if(flags_ & trap_nan) |
| throw std::ios_base::failure("NaN"); |
| else if((boost::math::signbit)(val)) |
| put_num_and_fill(it, iosb, "-", "nan", fill); |
| else if(iosb.flags() & std::ios_base::showpos) |
| put_num_and_fill(it, iosb, "+", "nan", fill); |
| else |
| put_num_and_fill(it, iosb, "", "nan", fill); |
| break; |
| |
| case FP_ZERO: |
| if(flags_ & signed_zero) { |
| if((boost::math::signbit)(val)) |
| put_num_and_fill(it, iosb, "-", "0", fill); |
| else if(iosb.flags() & std::ios_base::showpos) |
| put_num_and_fill(it, iosb, "+", "0", fill); |
| else |
| put_num_and_fill(it, iosb, "", "0", fill); |
| } |
| else |
| put_num_and_fill(it, iosb, "", "0", fill); |
| break; |
| |
| default: |
| it = std::num_put<CharType, OutputIterator>::do_put( |
| it, iosb, fill, val); |
| break; |
| } |
| } |
| |
| void put_num_and_fill( |
| OutputIterator& it, std::ios_base& iosb, const char* prefix, |
| const char* body, CharType fill) const |
| { |
| int width = (int)strlen(prefix) + (int)strlen(body); |
| std::ios_base::fmtflags adjust |
| = iosb.flags() & std::ios_base::adjustfield; |
| const std::ctype<CharType>& ct |
| = std::use_facet<std::ctype<CharType> >(iosb.getloc()); |
| |
| if(adjust != std::ios_base::internal && adjust != std::ios_base::left) |
| put_fill(it, iosb, fill, width); |
| |
| while(*prefix) |
| *it = ct.widen(*(prefix++)); |
| |
| if(adjust == std::ios_base::internal) |
| put_fill(it, iosb, fill, width); |
| |
| if(iosb.flags() & std::ios_base::uppercase) { |
| while(*body) |
| *it = ct.toupper(ct.widen(*(body++))); |
| } |
| else { |
| while(*body) |
| *it = ct.widen(*(body++)); |
| } |
| |
| if(adjust == std::ios_base::left) |
| put_fill(it, iosb, fill, width); |
| } |
| |
| void put_fill( |
| OutputIterator& it, std::ios_base& iosb, |
| CharType fill, int width) const |
| { |
| for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i) |
| *it = fill; |
| } |
| |
| private: |
| const int flags_; |
| }; |
| |
| |
| // class nonfinite_num_get ------------------------------------------------------ |
| |
| template< |
| class CharType, |
| class InputIterator = std::istreambuf_iterator<CharType> |
| > |
| class nonfinite_num_get : public std::num_get<CharType, InputIterator> |
| { |
| |
| public: |
| explicit nonfinite_num_get(int flags = 0) : flags_(flags) |
| {} |
| |
| protected: // float, double and long double versions of do_get. |
| virtual InputIterator do_get( |
| InputIterator it, InputIterator end, std::ios_base& iosb, |
| std::ios_base::iostate& state, float& val) const |
| { |
| get_and_check_eof(it, end, iosb, state, val); |
| return it; |
| } |
| |
| virtual InputIterator do_get( |
| InputIterator it, InputIterator end, std::ios_base& iosb, |
| std::ios_base::iostate& state, double& val) const |
| { |
| get_and_check_eof(it, end, iosb, state, val); |
| return it; |
| } |
| |
| virtual InputIterator do_get( |
| InputIterator it, InputIterator end, std::ios_base& iosb, |
| std::ios_base::iostate& state, long double& val) const |
| { |
| get_and_check_eof(it, end, iosb, state, val); |
| return it; |
| } |
| |
| //.............................................................................. |
| |
| private: |
| template<class ValType> static ValType positive_nan() |
| { |
| // On some platforms quiet_NaN() may be negative. |
| return (boost::math::copysign)( |
| std::numeric_limits<ValType>::quiet_NaN(), static_cast<ValType>(1) |
| ); |
| // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11 |
| } |
| |
| template<class ValType> void get_and_check_eof |
| ( |
| InputIterator& it, InputIterator end, std::ios_base& iosb, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| get_signed(it, end, iosb, state, val); |
| if(it == end) |
| state |= std::ios_base::eofbit; |
| } |
| |
| template<class ValType> void get_signed |
| ( |
| InputIterator& it, InputIterator end, std::ios_base& iosb, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| const std::ctype<CharType>& ct |
| = std::use_facet<std::ctype<CharType> >(iosb.getloc()); |
| |
| char c = peek_char(it, end, ct); |
| |
| bool negative = (c == '-'); |
| |
| if(negative || c == '+') |
| { |
| ++it; |
| c = peek_char(it, end, ct); |
| if(c == '-' || c == '+') |
| { // Without this check, "++5" etc would be accepted. |
| state |= std::ios_base::failbit; |
| return; |
| } |
| } |
| |
| get_unsigned(it, end, iosb, ct, state, val); |
| |
| if(negative) |
| { |
| val = (boost::math::changesign)(val); |
| } |
| } // void get_signed |
| |
| template<class ValType> void get_unsigned |
| ( //! Get an unsigned floating-point value into val, |
| //! but checking for letters indicating non-finites. |
| InputIterator& it, InputIterator end, std::ios_base& iosb, |
| const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| switch(peek_char(it, end, ct)) |
| { |
| case 'i': |
| get_i(it, end, ct, state, val); |
| break; |
| |
| case 'n': |
| get_n(it, end, ct, state, val); |
| break; |
| |
| case 'q': |
| case 's': |
| get_q(it, end, ct, state, val); |
| break; |
| |
| default: // Got a normal floating-point value into val. |
| it = std::num_get<CharType, InputIterator>::do_get( |
| it, end, iosb, state, val); |
| if((flags_ & legacy) && val == static_cast<ValType>(1) |
| && peek_char(it, end, ct) == '#') |
| get_one_hash(it, end, ct, state, val); |
| break; |
| } |
| } // get_unsigned |
| |
| //.......................................................................... |
| |
| template<class ValType> void get_i |
| ( // Get the rest of all strings starting with 'i', expect "inf", "infinity". |
| InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| if(!std::numeric_limits<ValType>::has_infinity |
| || (flags_ & trap_infinity)) |
| { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| ++it; |
| if(!match_string(it, end, ct, "nf")) |
| { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| if(peek_char(it, end, ct) != 'i') |
| { |
| val = std::numeric_limits<ValType>::infinity(); // "inf" |
| return; |
| } |
| |
| ++it; |
| if(!match_string(it, end, ct, "nity")) |
| { // Expected "infinity" |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| val = std::numeric_limits<ValType>::infinity(); // "infinity" |
| } // void get_i |
| |
| template<class ValType> void get_n |
| ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)" |
| InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| if(!std::numeric_limits<ValType>::has_quiet_NaN |
| || (flags_ & trap_nan)) { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| ++it; |
| if(!match_string(it, end, ct, "an")) |
| { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| switch(peek_char(it, end, ct)) { |
| case 'q': |
| case 's': |
| if(flags_ && legacy) |
| ++it; |
| break; // "nanq", "nans" |
| |
| case '(': // Optional payload field in (...) follows. |
| { |
| ++it; |
| char c; |
| while((c = peek_char(it, end, ct)) |
| && c != ')' && c != ' ' && c != '\n' && c != '\t') |
| ++it; |
| if(c != ')') |
| { // Optional payload field terminator missing! |
| state |= std::ios_base::failbit; |
| return; |
| } |
| ++it; |
| break; // "nan(...)" |
| } |
| |
| default: |
| break; // "nan" |
| } |
| |
| val = positive_nan<ValType>(); |
| } // void get_n |
| |
| template<class ValType> void get_q |
| ( // Get expected rest of string starting with 'q': "qnan". |
| InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| if(!std::numeric_limits<ValType>::has_quiet_NaN |
| || (flags_ & trap_nan) || !(flags_ & legacy)) |
| { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| ++it; |
| if(!match_string(it, end, ct, "nan")) |
| { |
| state |= std::ios_base::failbit; |
| return; |
| } |
| |
| val = positive_nan<ValType>(); // "QNAN" |
| } // void get_q |
| |
| template<class ValType> void get_one_hash |
| ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN". |
| InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| |
| ++it; |
| switch(peek_char(it, end, ct)) |
| { |
| case 'i': // from IND (indeterminate), considered same a QNAN. |
| get_one_hash_i(it, end, ct, state, val); // "1.#IND" |
| return; |
| |
| case 'q': // from QNAN |
| case 's': // from SNAN - treated the same as QNAN. |
| if(std::numeric_limits<ValType>::has_quiet_NaN |
| && !(flags_ & trap_nan)) |
| { |
| ++it; |
| if(match_string(it, end, ct, "nan")) |
| { // "1.#QNAN", "1.#SNAN" |
| // ++it; // removed as caused assert() cannot increment iterator). |
| // (match_string consumes string, so not needed?). |
| // https://svn.boost.org/trac/boost/ticket/5467 |
| // Change in nonfinite_num_facet.hpp Paul A. Bristow 11 Apr 11 makes legacy_test.cpp work OK. |
| val = positive_nan<ValType>(); // "1.#QNAN" |
| return; |
| } |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| state |= std::ios_base::failbit; |
| } // void get_one_hash |
| |
| template<class ValType> void get_one_hash_i |
| ( // Get expected strings after 'i', "1.#INF", 1.#IND". |
| InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, |
| std::ios_base::iostate& state, ValType& val |
| ) const |
| { |
| ++it; |
| |
| if(peek_char(it, end, ct) == 'n') |
| { |
| ++it; |
| switch(peek_char(it, end, ct)) |
| { |
| case 'f': // "1.#INF" |
| if(std::numeric_limits<ValType>::has_infinity |
| && !(flags_ & trap_infinity)) |
| { |
| ++it; |
| val = std::numeric_limits<ValType>::infinity(); |
| return; |
| } |
| break; |
| |
| case 'd': // 1.#IND" |
| if(std::numeric_limits<ValType>::has_quiet_NaN |
| && !(flags_ & trap_nan)) |
| { |
| ++it; |
| val = positive_nan<ValType>(); |
| return; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| state |= std::ios_base::failbit; |
| } // void get_one_hash_i |
| |
| //.......................................................................... |
| |
| char peek_char |
| ( //! \return next char in the input buffer, ensuring lowercase (but do not 'consume' char). |
| InputIterator& it, InputIterator end, |
| const std::ctype<CharType>& ct |
| ) const |
| { |
| if(it == end) return 0; |
| return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive. |
| } |
| |
| bool match_string |
| ( //! Match remaining chars to expected string (case insensitive), |
| //! consuming chars that match OK. |
| //! \return true if matched expected string, else false. |
| InputIterator& it, InputIterator end, |
| const std::ctype<CharType>& ct, |
| const char* s |
| ) const |
| { |
| while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0)) |
| { |
| ++s; |
| ++it; // |
| } |
| return !*s; |
| } // bool match_string |
| |
| private: |
| const int flags_; |
| }; // |
| |
| //------------------------------------------------------------------------------ |
| |
| } // namespace math |
| } // namespace boost |
| |
| #ifdef _MSC_VER |
| # pragma warning(pop) |
| #endif |
| |
| #endif |