#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 #include #include #include #include #include #include #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 > class nonfinite_num_put : public std::num_put { 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 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 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::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& ct = std::use_facet >(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(width); i > 0; --i) *it = fill; } private: const int flags_; }; // class nonfinite_num_get ------------------------------------------------------ template< class CharType, class InputIterator = std::istreambuf_iterator > class nonfinite_num_get : public std::num_get { 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 static ValType positive_nan() { // On some platforms quiet_NaN() may be negative. return (boost::math::copysign)( std::numeric_limits::quiet_NaN(), static_cast(1) ); // static_cast(1) added Paul A. Bristow 5 Apr 11 } template 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 void get_signed ( InputIterator& it, InputIterator end, std::ios_base& iosb, std::ios_base::iostate& state, ValType& val ) const { const std::ctype& ct = std::use_facet >(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 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& 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::do_get( it, end, iosb, state, val); if((flags_ & legacy) && val == static_cast(1) && peek_char(it, end, ct) == '#') get_one_hash(it, end, ct, state, val); break; } } // get_unsigned //.......................................................................... template void get_i ( // Get the rest of all strings starting with 'i', expect "inf", "infinity". InputIterator& it, InputIterator end, const std::ctype& ct, std::ios_base::iostate& state, ValType& val ) const { if(!std::numeric_limits::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::infinity(); // "inf" return; } ++it; if(!match_string(it, end, ct, "nity")) { // Expected "infinity" state |= std::ios_base::failbit; return; } val = std::numeric_limits::infinity(); // "infinity" } // void get_i template void get_n ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)" InputIterator& it, InputIterator end, const std::ctype& ct, std::ios_base::iostate& state, ValType& val ) const { if(!std::numeric_limits::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(); } // void get_n template void get_q ( // Get expected rest of string starting with 'q': "qnan". InputIterator& it, InputIterator end, const std::ctype& ct, std::ios_base::iostate& state, ValType& val ) const { if(!std::numeric_limits::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(); // "QNAN" } // void get_q template void get_one_hash ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN". InputIterator& it, InputIterator end, const std::ctype& 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::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(); // "1.#QNAN" return; } } break; default: break; } state |= std::ios_base::failbit; } // void get_one_hash template void get_one_hash_i ( // Get expected strings after 'i', "1.#INF", 1.#IND". InputIterator& it, InputIterator end, const std::ctype& 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::has_infinity && !(flags_ & trap_infinity)) { ++it; val = std::numeric_limits::infinity(); return; } break; case 'd': // 1.#IND" if(std::numeric_limits::has_quiet_NaN && !(flags_ & trap_nan)) { ++it; val = positive_nan(); 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& 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& 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