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