From e3e838da6c05fdcf7fc06a2b2fa923bbfe136dec Mon Sep 17 00:00:00 2001 From: "Paul A. Bristow" Date: Mon, 18 Apr 2011 16:19:48 +0000 Subject: [PATCH] nonfinite facets [SVN r71371] --- .../nonfinite_num_facets.hpp | 540 ++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 include/boost/math/special_functions/nonfinite_num_facets.hpp diff --git a/include/boost/math/special_functions/nonfinite_num_facets.hpp b/include/boost/math/special_functions/nonfinite_num_facets.hpp new file mode 100644 index 000000000..c4bbe1601 --- /dev/null +++ b/include/boost/math/special_functions/nonfinite_num_facets.hpp @@ -0,0 +1,540 @@ +#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: + 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 value into val + 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: + 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 (from input buffer). + 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). + 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; + } // char peek_char + + private: + const int flags_; + }; // + + //------------------------------------------------------------------------------ + + } // namespace math +} // namespace boost + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#endif