Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:46:08

0001 #ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP
0002 #define BOOST_MATH_NONFINITE_NUM_FACETS_HPP
0003 
0004 // Copyright 2006 Johan Rade
0005 // Copyright 2012 K R Walker
0006 // Copyright 2011, 2012 Paul A. Bristow 
0007 
0008 // Distributed under the Boost Software License, Version 1.0.
0009 // (See accompanying file LICENSE_1_0.txt
0010 // or copy at http://www.boost.org/LICENSE_1_0.txt)
0011 
0012 /*
0013 \file
0014 
0015 \brief non_finite_num facets for C99 standard output of infinity and NaN.
0016 
0017 \details See fuller documentation at Boost.Math Facets
0018   for Floating-Point Infinities and NaNs.
0019 */
0020 
0021 #include <cstring>
0022 #include <ios>
0023 #include <limits>
0024 #include <locale>
0025 #include <boost/math/tools/throw_exception.hpp>
0026 #include <boost/math/special_functions/fpclassify.hpp>
0027 #include <boost/math/special_functions/sign.hpp>
0028 
0029 #ifdef _MSC_VER
0030 #  pragma warning(push)
0031 #  pragma warning(disable : 4127) // conditional expression is constant.
0032 #  pragma warning(disable : 4706) // assignment within conditional expression.
0033 #endif
0034 
0035 namespace boost {
0036   namespace math {
0037 
0038     // flags (enums can be ORed together)       -----------------------------------
0039 
0040     const int legacy = 0x1; //!< get facet will recognize most string representations of infinity and NaN.
0041     const int signed_zero = 0x2; //!< put facet will distinguish between positive and negative zero.
0042     const int trap_infinity = 0x4; /*!< put facet will throw an exception of type std::ios_base::failure
0043        when an attempt is made to format positive or negative infinity.
0044        get will set the fail bit of the stream when an attempt is made
0045        to parse a string that represents positive or negative sign infinity.
0046     */
0047     const int trap_nan = 0x8; /*!< put facet will throw an exception of type std::ios_base::failure
0048        when an attempt is made to format positive or negative NaN.
0049        get will set the fail bit of the stream when an attempt is made
0050        to parse a string that represents positive or negative sign infinity.
0051        */
0052 
0053     // class nonfinite_num_put -----------------------------------------------------
0054 
0055     template<
0056       class CharType,
0057       class OutputIterator = std::ostreambuf_iterator<CharType>
0058             >
0059     class nonfinite_num_put : public std::num_put<CharType, OutputIterator>
0060     {
0061     public:
0062       explicit nonfinite_num_put(int flags = 0) : flags_(flags) {}
0063 
0064     protected:
0065       virtual OutputIterator do_put(
0066         OutputIterator it, std::ios_base& iosb, CharType fill, double val) const
0067       {
0068         put_and_reset_width(it, iosb, fill, val);
0069         return it;
0070       }
0071 
0072       virtual OutputIterator do_put(
0073         OutputIterator it, std::ios_base& iosb,  CharType fill, long double val) const
0074       {
0075         put_and_reset_width(it, iosb, fill, val);
0076         return it;
0077       }
0078 
0079     private:
0080       template<class ValType> void put_and_reset_width(
0081         OutputIterator& it, std::ios_base& iosb,
0082         CharType fill, ValType val) const
0083       {
0084         put_impl(it, iosb, fill, val);
0085         iosb.width(0);
0086       }
0087 
0088       template<class ValType> void put_impl(
0089         OutputIterator& it, std::ios_base& iosb,
0090         CharType fill, ValType val) const
0091       {
0092         static const CharType prefix_plus[2] = { '+', '\0' };
0093         static const CharType prefix_minus[2] = { '-', '\0' };
0094         static const CharType body_inf[4] = { 'i', 'n', 'f', '\0' };
0095         static const CharType body_nan[4] = { 'n', 'a', 'n', '\0' };
0096         static const CharType* null_string = 0;
0097 
0098         switch((boost::math::fpclassify)(val))
0099         {
0100 
0101         case FP_INFINITE:
0102           if(flags_ & trap_infinity)
0103           {
0104             BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("Infinity"));
0105           }
0106           else if((boost::math::signbit)(val))
0107           { // negative infinity.
0108             put_num_and_fill(it, iosb, prefix_minus, body_inf, fill, val);
0109           }
0110           else if(iosb.flags() & std::ios_base::showpos)
0111           { // Explicit "+inf" wanted.
0112             put_num_and_fill(it, iosb, prefix_plus, body_inf, fill, val);
0113           }
0114           else
0115           { // just "inf" wanted.
0116             put_num_and_fill(it, iosb, null_string, body_inf, fill, val);
0117           }
0118           break;
0119 
0120         case FP_NAN:
0121           if(flags_ & trap_nan)
0122           {
0123             BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("NaN"));
0124           }
0125           else if((boost::math::signbit)(val))
0126           { // negative so "-nan".
0127             put_num_and_fill(it, iosb, prefix_minus, body_nan, fill, val);
0128           }
0129           else if(iosb.flags() & std::ios_base::showpos)
0130           { // explicit "+nan" wanted.
0131             put_num_and_fill(it, iosb, prefix_plus, body_nan, fill, val);
0132           }
0133           else
0134           { // Just "nan".
0135             put_num_and_fill(it, iosb, null_string, body_nan, fill, val);
0136           }
0137           break;
0138 
0139         case FP_ZERO:
0140           if((flags_ & signed_zero) && ((boost::math::signbit)(val)))
0141           { // Flag set to distinguish between positive and negative zero.
0142             // But string "0" should have stuff after decimal point if setprecision and/or exp format. 
0143 
0144             std::basic_ostringstream<CharType> zeros; // Needs to be CharType version.
0145 
0146             // Copy flags, fill, width and precision.
0147             zeros.flags(iosb.flags());
0148             zeros.unsetf(std::ios::showpos); // Ignore showpos because must be negative.
0149             zeros.precision(iosb.precision());
0150             //zeros.width is set by put_num_and_fill
0151             zeros.fill(static_cast<char>(fill));
0152             zeros << ValType(0);
0153             put_num_and_fill(it, iosb, prefix_minus, zeros.str().c_str(), fill, val);
0154           }
0155           else
0156           { // Output the platform default for positive and negative zero.
0157             put_num_and_fill(it, iosb, null_string, null_string, fill, val);
0158           }
0159           break;
0160 
0161         default:  // Normal non-zero finite value.
0162           it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
0163           break;
0164         }
0165       }
0166 
0167       template<class ValType>
0168       void put_num_and_fill(
0169         OutputIterator& it, std::ios_base& iosb, const CharType* prefix,
0170           const CharType* body, CharType fill, ValType val) const
0171       {
0172         int prefix_length = prefix ? (int)std::char_traits<CharType>::length(prefix) : 0;
0173         int body_length = body ? (int)std::char_traits<CharType>::length(body) : 0;
0174         int width = prefix_length + body_length;
0175         std::ios_base::fmtflags adjust = iosb.flags() & std::ios_base::adjustfield;
0176         const std::ctype<CharType>& ct
0177           = std::use_facet<std::ctype<CharType> >(iosb.getloc());
0178 
0179         if(body || prefix)
0180         { // adjust == std::ios_base::right, so leading fill needed.
0181           if(adjust != std::ios_base::internal && adjust != std::ios_base::left)
0182             put_fill(it, iosb, fill, width);
0183         }
0184 
0185         if(prefix)
0186         { // Adjust width for prefix.
0187           while(*prefix)
0188             *it = *(prefix++);
0189           iosb.width( iosb.width() - prefix_length );
0190           width -= prefix_length;
0191         }
0192 
0193         if(body)
0194         { // 
0195           if(adjust == std::ios_base::internal)
0196           { // Put fill between sign and digits.
0197             put_fill(it, iosb, fill, width);
0198           }
0199           if(iosb.flags() & std::ios_base::uppercase)
0200           {
0201               while(*body)
0202                 *it = ct.toupper(*(body++));
0203           }
0204           else
0205           {
0206             while(*body)
0207               *it = *(body++);
0208           }
0209 
0210           if(adjust == std::ios_base::left)
0211             put_fill(it, iosb, fill, width);
0212         }
0213         else
0214         {
0215           it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
0216         }
0217       }
0218 
0219       void put_fill(
0220         OutputIterator& it, std::ios_base& iosb, CharType fill, int width) const
0221       { // Insert fill chars.
0222         for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i)
0223           *it = fill;
0224       }
0225 
0226       const int flags_;
0227     };
0228 
0229 
0230     // class nonfinite_num_get ------------------------------------------------------
0231 
0232     template<
0233       class CharType,
0234       class InputIterator = std::istreambuf_iterator<CharType>
0235     >
0236     class nonfinite_num_get : public std::num_get<CharType, InputIterator>
0237     {
0238 
0239     public:
0240       explicit nonfinite_num_get(int flags = 0) : flags_(flags)
0241       {}
0242 
0243     protected:  // float, double and long double versions of do_get.
0244       virtual InputIterator do_get(
0245         InputIterator it, InputIterator end, std::ios_base& iosb,
0246         std::ios_base::iostate& state, float& val) const
0247       {
0248         get_and_check_eof(it, end, iosb, state, val);
0249         return it;
0250       }
0251 
0252       virtual InputIterator do_get(
0253         InputIterator it, InputIterator end, std::ios_base& iosb,
0254         std::ios_base::iostate& state, double& val) const
0255       {
0256         get_and_check_eof(it, end, iosb, state, val);
0257         return it;
0258       }
0259 
0260       virtual InputIterator do_get(
0261         InputIterator it, InputIterator end, std::ios_base& iosb,
0262         std::ios_base::iostate& state, long double& val) const
0263       {
0264         get_and_check_eof(it, end, iosb, state, val);
0265         return it;
0266       }
0267 
0268       //..............................................................................
0269 
0270     private:
0271       template<class ValType> static ValType positive_nan()
0272       {
0273         // On some platforms quiet_NaN() may be negative.
0274         return (boost::math::copysign)(
0275           std::numeric_limits<ValType>::quiet_NaN(), static_cast<ValType>(1)
0276           );
0277         // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11
0278       }
0279 
0280       template<class ValType> void get_and_check_eof
0281       (
0282         InputIterator& it, InputIterator end, std::ios_base& iosb,
0283         std::ios_base::iostate& state, ValType& val
0284       ) const
0285       {
0286         get_signed(it, end, iosb, state, val);
0287         if(it == end)
0288           state |= std::ios_base::eofbit;
0289       }
0290 
0291       template<class ValType> void get_signed
0292       (
0293         InputIterator& it, InputIterator end, std::ios_base& iosb,
0294         std::ios_base::iostate& state, ValType& val
0295       ) const
0296       {
0297         const std::ctype<CharType>& ct
0298           = std::use_facet<std::ctype<CharType> >(iosb.getloc());
0299 
0300         char c = peek_char(it, end, ct);
0301 
0302         bool negative = (c == '-');
0303 
0304         if(negative || c == '+')
0305         {
0306           ++it;
0307           c = peek_char(it, end, ct);
0308           if(c == '-' || c == '+')
0309           { // Without this check, "++5" etc would be accepted.
0310             state |= std::ios_base::failbit;
0311             return;
0312           }
0313         }
0314 
0315         get_unsigned(it, end, iosb, ct, state, val);
0316 
0317         if(negative)
0318         {
0319           val = (boost::math::changesign)(val);
0320         }
0321       } // void get_signed
0322 
0323       template<class ValType> void get_unsigned
0324       ( //! Get an unsigned floating-point value into val,
0325         //! but checking for letters indicating non-finites.
0326         InputIterator& it, InputIterator end, std::ios_base& iosb,
0327         const std::ctype<CharType>& ct,
0328         std::ios_base::iostate& state, ValType& val
0329       ) const
0330       {
0331         switch(peek_char(it, end, ct))
0332         {
0333         case 'i':
0334           get_i(it, end, ct, state, val);
0335           break;
0336 
0337         case 'n':
0338           get_n(it, end, ct, state, val);
0339           break;
0340 
0341         case 'q':
0342         case 's':
0343           get_q(it, end, ct, state, val);
0344           break;
0345 
0346         default: // Got a normal floating-point value into val.
0347           it = std::num_get<CharType, InputIterator>::do_get(
0348             it, end, iosb, state, val);
0349           if((flags_ & legacy) && val == static_cast<ValType>(1)
0350             && peek_char(it, end, ct) == '#')
0351             get_one_hash(it, end, ct, state, val);
0352           break;
0353         }
0354       } //  get_unsigned
0355 
0356       //..........................................................................
0357 
0358       template<class ValType> void get_i
0359       ( // Get the rest of all strings starting with 'i', expect "inf", "infinity".
0360         InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
0361         std::ios_base::iostate& state, ValType& val
0362       ) const
0363       {
0364         if(!std::numeric_limits<ValType>::has_infinity
0365           || (flags_ & trap_infinity))
0366         {
0367             state |= std::ios_base::failbit;
0368             return;
0369         }
0370 
0371         ++it;
0372         if(!match_string(it, end, ct, "nf"))
0373         {
0374           state |= std::ios_base::failbit;
0375           return;
0376         }
0377 
0378         if(peek_char(it, end, ct) != 'i')
0379         {
0380           val = std::numeric_limits<ValType>::infinity();  // "inf"
0381           return;
0382         }
0383 
0384         ++it;
0385         if(!match_string(it, end, ct, "nity"))
0386         { // Expected "infinity"
0387           state |= std::ios_base::failbit;
0388           return;
0389         }
0390 
0391         val = std::numeric_limits<ValType>::infinity(); // "infinity"
0392       } // void get_i
0393 
0394       template<class ValType> void get_n
0395       ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)"
0396         InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
0397         std::ios_base::iostate& state, ValType& val
0398       ) const
0399       {
0400         if(!std::numeric_limits<ValType>::has_quiet_NaN
0401           || (flags_ & trap_nan)) {
0402             state |= std::ios_base::failbit;
0403             return;
0404         }
0405 
0406         ++it;
0407         if(!match_string(it, end, ct, "an"))
0408         {
0409           state |= std::ios_base::failbit;
0410           return;
0411         }
0412 
0413         switch(peek_char(it, end, ct)) {
0414         case 'q':
0415         case 's':
0416           if(flags_ & legacy)
0417             ++it;
0418           break;  // "nanq", "nans"
0419 
0420         case '(':   // Optional payload field in (...) follows.
0421          {
0422             ++it;
0423             char c;
0424             while((c = peek_char(it, end, ct))
0425               && c != ')' && c != ' ' && c != '\n' && c != '\t')
0426               ++it;
0427             if(c != ')')
0428             { // Optional payload field terminator missing!
0429               state |= std::ios_base::failbit;
0430               return;
0431             }
0432             ++it;
0433             break;  // "nan(...)"
0434           }
0435 
0436         default:
0437           break;  // "nan"
0438         }
0439 
0440         val = positive_nan<ValType>();
0441       } // void get_n
0442 
0443       template<class ValType> void get_q
0444       ( // Get expected rest of string starting with 'q': "qnan".
0445         InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
0446         std::ios_base::iostate& state, ValType& val
0447       ) const
0448       {
0449         if(!std::numeric_limits<ValType>::has_quiet_NaN
0450           || (flags_ & trap_nan) || !(flags_ & legacy))
0451         {
0452           state |= std::ios_base::failbit;
0453           return;
0454         }
0455 
0456         ++it;
0457         if(!match_string(it, end, ct, "nan"))
0458         {
0459           state |= std::ios_base::failbit;
0460           return;
0461         }
0462 
0463         val = positive_nan<ValType>(); // "QNAN"
0464       } //  void get_q
0465 
0466       template<class ValType> void get_one_hash
0467       ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN".
0468         InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
0469         std::ios_base::iostate& state, ValType& val
0470       ) const
0471       {
0472 
0473         ++it;
0474         switch(peek_char(it, end, ct))
0475         {
0476         case 'i': // from IND (indeterminate), considered same a QNAN.
0477           get_one_hash_i(it, end, ct, state, val); // "1.#IND"
0478           return;
0479 
0480         case 'q': // from QNAN
0481         case 's': // from SNAN - treated the same as QNAN.
0482           if(std::numeric_limits<ValType>::has_quiet_NaN
0483             && !(flags_ & trap_nan))
0484           {
0485             ++it;
0486             if(match_string(it, end, ct, "nan"))
0487             { // "1.#QNAN", "1.#SNAN"
0488  //             ++it; // removed as caused assert() cannot increment iterator).
0489 // (match_string consumes string, so not needed?).
0490 // https://svn.boost.org/trac/boost/ticket/5467
0491 // Change in nonfinite_num_facet.hpp Paul A. Bristow 11 Apr 11 makes legacy_test.cpp work OK.
0492               val = positive_nan<ValType>(); // "1.#QNAN"
0493               return;
0494             }
0495           }
0496           break;
0497 
0498         default:
0499           break;
0500         }
0501 
0502         state |= std::ios_base::failbit;
0503       } //  void get_one_hash
0504 
0505       template<class ValType> void get_one_hash_i
0506       ( // Get expected strings after 'i', "1.#INF", 1.#IND".
0507         InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,
0508         std::ios_base::iostate& state, ValType& val
0509       ) const
0510       {
0511         ++it;
0512 
0513         if(peek_char(it, end, ct) == 'n')
0514         {
0515           ++it;
0516           switch(peek_char(it, end, ct))
0517           {
0518           case 'f':  // "1.#INF"
0519             if(std::numeric_limits<ValType>::has_infinity
0520               && !(flags_ & trap_infinity))
0521             {
0522                 ++it;
0523                 val = std::numeric_limits<ValType>::infinity();
0524                 return;
0525             }
0526             break;
0527 
0528           case 'd':   // 1.#IND"
0529             if(std::numeric_limits<ValType>::has_quiet_NaN
0530               && !(flags_ & trap_nan))
0531             {
0532                 ++it;
0533                 val = positive_nan<ValType>();
0534                 return;
0535             }
0536             break;
0537 
0538           default:
0539             break;
0540           }
0541         }
0542 
0543         state |= std::ios_base::failbit;
0544       } //  void get_one_hash_i
0545 
0546       //..........................................................................
0547 
0548       char peek_char
0549       ( //! \return next char in the input buffer, ensuring lowercase (but do not 'consume' char).
0550         InputIterator& it, InputIterator end,
0551         const std::ctype<CharType>& ct
0552       ) const
0553       {
0554         if(it == end) return 0;
0555         return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive.
0556       }
0557 
0558       bool match_string
0559       ( //! Match remaining chars to expected string (case insensitive),
0560         //! consuming chars that match OK.
0561         //! \return true if matched expected string, else false.
0562         InputIterator& it, InputIterator end,
0563         const std::ctype<CharType>& ct,
0564         const char* s
0565       ) const
0566       {
0567         while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0))
0568         {
0569           ++s;
0570           ++it; //
0571         }
0572         return !*s;
0573       } // bool match_string
0574 
0575       const int flags_;
0576     }; //
0577 
0578     //------------------------------------------------------------------------------
0579 
0580   }   // namespace math
0581 }   // namespace boost
0582 
0583 #ifdef _MSC_VER
0584 #   pragma warning(pop)
0585 #endif
0586 
0587 #endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP
0588