123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- #ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP
- #define BOOST_MATH_NONFINITE_NUM_FACETS_HPP
- // Copyright 2006 Johan Rade
- // Copyright 2012 K R Walker
- // Copyright 2011, 2012 Paul A. Bristow
- // 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/math/tools/throw_exception.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.
- #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
- {
- static const CharType prefix_plus[2] = { '+', '\0' };
- static const CharType prefix_minus[2] = { '-', '\0' };
- static const CharType body_inf[4] = { 'i', 'n', 'f', '\0' };
- static const CharType body_nan[4] = { 'n', 'a', 'n', '\0' };
- static const CharType* null_string = 0;
- switch((boost::math::fpclassify)(val))
- {
- case FP_INFINITE:
- if(flags_ & trap_infinity)
- {
- BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("Infinity"));
- }
- else if((boost::math::signbit)(val))
- { // negative infinity.
- put_num_and_fill(it, iosb, prefix_minus, body_inf, fill, val);
- }
- else if(iosb.flags() & std::ios_base::showpos)
- { // Explicit "+inf" wanted.
- put_num_and_fill(it, iosb, prefix_plus, body_inf, fill, val);
- }
- else
- { // just "inf" wanted.
- put_num_and_fill(it, iosb, null_string, body_inf, fill, val);
- }
- break;
- case FP_NAN:
- if(flags_ & trap_nan)
- {
- BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("NaN"));
- }
- else if((boost::math::signbit)(val))
- { // negative so "-nan".
- put_num_and_fill(it, iosb, prefix_minus, body_nan, fill, val);
- }
- else if(iosb.flags() & std::ios_base::showpos)
- { // explicit "+nan" wanted.
- put_num_and_fill(it, iosb, prefix_plus, body_nan, fill, val);
- }
- else
- { // Just "nan".
- put_num_and_fill(it, iosb, null_string, body_nan, fill, val);
- }
- break;
- case FP_ZERO:
- if((flags_ & signed_zero) && ((boost::math::signbit)(val)))
- { // Flag set to distinguish between positive and negative zero.
- // But string "0" should have stuff after decimal point if setprecision and/or exp format.
- std::basic_ostringstream<CharType> zeros; // Needs to be CharType version.
- // Copy flags, fill, width and precision.
- zeros.flags(iosb.flags());
- zeros.unsetf(std::ios::showpos); // Ignore showpos because must be negative.
- zeros.precision(iosb.precision());
- //zeros.width is set by put_num_and_fill
- zeros.fill(static_cast<char>(fill));
- zeros << ValType(0);
- put_num_and_fill(it, iosb, prefix_minus, zeros.str().c_str(), fill, val);
- }
- else
- { // Output the platform default for positive and negative zero.
- put_num_and_fill(it, iosb, null_string, null_string, fill, val);
- }
- break;
- default: // Normal non-zero finite value.
- it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
- break;
- }
- }
- template<class ValType>
- void put_num_and_fill(
- OutputIterator& it, std::ios_base& iosb, const CharType* prefix,
- const CharType* body, CharType fill, ValType val) const
- {
- int prefix_length = prefix ? (int)std::char_traits<CharType>::length(prefix) : 0;
- int body_length = body ? (int)std::char_traits<CharType>::length(body) : 0;
- int width = prefix_length + body_length;
- 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(body || prefix)
- { // adjust == std::ios_base::right, so leading fill needed.
- if(adjust != std::ios_base::internal && adjust != std::ios_base::left)
- put_fill(it, iosb, fill, width);
- }
- if(prefix)
- { // Adjust width for prefix.
- while(*prefix)
- *it = *(prefix++);
- iosb.width( iosb.width() - prefix_length );
- width -= prefix_length;
- }
- if(body)
- { //
- if(adjust == std::ios_base::internal)
- { // Put fill between sign and digits.
- put_fill(it, iosb, fill, width);
- }
- if(iosb.flags() & std::ios_base::uppercase)
- {
- while(*body)
- *it = ct.toupper(*(body++));
- }
- else
- {
- while(*body)
- *it = *(body++);
- }
- if(adjust == std::ios_base::left)
- put_fill(it, iosb, fill, width);
- }
- else
- {
- it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
- }
- }
- void put_fill(
- OutputIterator& it, std::ios_base& iosb, CharType fill, int width) const
- { // Insert fill chars.
- for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i)
- *it = fill;
- }
- 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
- const int flags_;
- }; //
- //------------------------------------------------------------------------------
- } // namespace math
- } // namespace boost
- #ifdef _MSC_VER
- # pragma warning(pop)
- #endif
- #endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP
|