brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.5 KiB · 5e08c10 Raw
589 lines · plain
1#ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP2#define BOOST_MATH_NONFINITE_NUM_FACETS_HPP3 4// Copyright 2006 Johan Rade5// Copyright 2012 K R Walker6// Copyright 2011, 2012 Paul A. Bristow 7 8// Distributed under the Boost Software License, Version 1.0.9// (See accompanying file LICENSE_1_0.txt10// or copy at http://www.boost.org/LICENSE_1_0.txt)11 12/*13\file14 15\brief non_finite_num facets for C99 standard output of infinity and NaN.16 17\details See fuller documentation at Boost.Math Facets18  for Floating-Point Infinities and NaNs.19*/20 21#include <cstring>22#include <ios>23#include <limits>24#include <locale>25#include <boost/math/tools/throw_exception.hpp>26#include <boost/math/special_functions/fpclassify.hpp>27#include <boost/math/special_functions/sign.hpp>28 29#ifdef _MSC_VER30#  pragma warning(push)31#  pragma warning(disable : 4127) // conditional expression is constant.32#  pragma warning(disable : 4706) // assignment within conditional expression.33#endif34 35namespace boost {36  namespace math {37 38    // flags (enums can be ORed together)       -----------------------------------39 40    const int legacy = 0x1; //!< get facet will recognize most string representations of infinity and NaN.41    const int signed_zero = 0x2; //!< put facet will distinguish between positive and negative zero.42    const int trap_infinity = 0x4; /*!< put facet will throw an exception of type std::ios_base::failure43       when an attempt is made to format positive or negative infinity.44       get will set the fail bit of the stream when an attempt is made45       to parse a string that represents positive or negative sign infinity.46    */47    const int trap_nan = 0x8; /*!< put facet will throw an exception of type std::ios_base::failure48       when an attempt is made to format positive or negative NaN.49       get will set the fail bit of the stream when an attempt is made50       to parse a string that represents positive or negative sign infinity.51       */52 53    // class nonfinite_num_put -----------------------------------------------------54 55    template<56      class CharType,57      class OutputIterator = std::ostreambuf_iterator<CharType>58            >59    class nonfinite_num_put : public std::num_put<CharType, OutputIterator>60    {61    public:62      explicit nonfinite_num_put(int flags = 0) : flags_(flags) {}63 64    protected:65      virtual OutputIterator do_put(66        OutputIterator it, std::ios_base& iosb, CharType fill, double val) const67      {68        put_and_reset_width(it, iosb, fill, val);69        return it;70      }71 72      virtual OutputIterator do_put(73        OutputIterator it, std::ios_base& iosb,  CharType fill, long double val) const74      {75        put_and_reset_width(it, iosb, fill, val);76        return it;77      }78 79    private:80      template<class ValType> void put_and_reset_width(81        OutputIterator& it, std::ios_base& iosb,82        CharType fill, ValType val) const83      {84        put_impl(it, iosb, fill, val);85        iosb.width(0);86      }87 88      template<class ValType> void put_impl(89        OutputIterator& it, std::ios_base& iosb,90        CharType fill, ValType val) const91      {92        static const CharType prefix_plus[2] = { '+', '\0' };93        static const CharType prefix_minus[2] = { '-', '\0' };94        static const CharType body_inf[4] = { 'i', 'n', 'f', '\0' };95        static const CharType body_nan[4] = { 'n', 'a', 'n', '\0' };96        static const CharType* null_string = 0;97 98        switch((boost::math::fpclassify)(val))99        {100 101        case FP_INFINITE:102          if(flags_ & trap_infinity)103          {104            BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("Infinity"));105          }106          else if((boost::math::signbit)(val))107          { // negative infinity.108            put_num_and_fill(it, iosb, prefix_minus, body_inf, fill, val);109          }110          else if(iosb.flags() & std::ios_base::showpos)111          { // Explicit "+inf" wanted.112            put_num_and_fill(it, iosb, prefix_plus, body_inf, fill, val);113          }114          else115          { // just "inf" wanted.116            put_num_and_fill(it, iosb, null_string, body_inf, fill, val);117          }118          break;119 120        case FP_NAN:121          if(flags_ & trap_nan)122          {123            BOOST_MATH_THROW_EXCEPTION(std::ios_base::failure("NaN"));124          }125          else if((boost::math::signbit)(val))126          { // negative so "-nan".127            put_num_and_fill(it, iosb, prefix_minus, body_nan, fill, val);128          }129          else if(iosb.flags() & std::ios_base::showpos)130          { // explicit "+nan" wanted.131            put_num_and_fill(it, iosb, prefix_plus, body_nan, fill, val);132          }133          else134          { // Just "nan".135            put_num_and_fill(it, iosb, null_string, body_nan, fill, val);136          }137          break;138 139        case FP_ZERO:140          if((flags_ & signed_zero) && ((boost::math::signbit)(val)))141          { // Flag set to distinguish between positive and negative zero.142            // But string "0" should have stuff after decimal point if setprecision and/or exp format. 143 144            std::basic_ostringstream<CharType> zeros; // Needs to be CharType version.145 146            // Copy flags, fill, width and precision.147            zeros.flags(iosb.flags());148            zeros.unsetf(std::ios::showpos); // Ignore showpos because must be negative.149            zeros.precision(iosb.precision());150            //zeros.width is set by put_num_and_fill151            zeros.fill(static_cast<char>(fill));152            zeros << ValType(0);153            put_num_and_fill(it, iosb, prefix_minus, zeros.str().c_str(), fill, val);154          }155          else156          { // Output the platform default for positive and negative zero.157            put_num_and_fill(it, iosb, null_string, null_string, fill, val);158          }159          break;160 161        default:  // Normal non-zero finite value.162          it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);163          break;164        }165      }166 167      template<class ValType>168      void put_num_and_fill(169        OutputIterator& it, std::ios_base& iosb, const CharType* prefix,170          const CharType* body, CharType fill, ValType val) const171      {172        int prefix_length = prefix ? (int)std::char_traits<CharType>::length(prefix) : 0;173        int body_length = body ? (int)std::char_traits<CharType>::length(body) : 0;174        int width = prefix_length + body_length;175        std::ios_base::fmtflags adjust = iosb.flags() & std::ios_base::adjustfield;176        const std::ctype<CharType>& ct177          = std::use_facet<std::ctype<CharType> >(iosb.getloc());178 179        if(body || prefix)180        { // adjust == std::ios_base::right, so leading fill needed.181          if(adjust != std::ios_base::internal && adjust != std::ios_base::left)182            put_fill(it, iosb, fill, width);183        }184 185        if(prefix)186        { // Adjust width for prefix.187          while(*prefix)188            *it = *(prefix++);189          iosb.width( iosb.width() - prefix_length );190          width -= prefix_length;191        }192 193        if(body)194        { // 195          if(adjust == std::ios_base::internal)196          { // Put fill between sign and digits.197            put_fill(it, iosb, fill, width);198          }199          if(iosb.flags() & std::ios_base::uppercase)200          {201              while(*body)202                *it = ct.toupper(*(body++));203          }204          else205          {206            while(*body)207              *it = *(body++);208          }209 210          if(adjust == std::ios_base::left)211            put_fill(it, iosb, fill, width);212        }213        else214        {215          it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);216        }217      }218 219      void put_fill(220        OutputIterator& it, std::ios_base& iosb, CharType fill, int width) const221      { // Insert fill chars.222        for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i)223          *it = fill;224      }225 226      const int flags_;227    };228 229 230    // class nonfinite_num_get ------------------------------------------------------231 232    template<233      class CharType,234      class InputIterator = std::istreambuf_iterator<CharType>235    >236    class nonfinite_num_get : public std::num_get<CharType, InputIterator>237    {238 239    public:240      explicit nonfinite_num_get(int flags = 0) : flags_(flags)241      {}242 243    protected:  // float, double and long double versions of do_get.244      virtual InputIterator do_get(245        InputIterator it, InputIterator end, std::ios_base& iosb,246        std::ios_base::iostate& state, float& val) const247      {248        get_and_check_eof(it, end, iosb, state, val);249        return it;250      }251 252      virtual InputIterator do_get(253        InputIterator it, InputIterator end, std::ios_base& iosb,254        std::ios_base::iostate& state, double& val) const255      {256        get_and_check_eof(it, end, iosb, state, val);257        return it;258      }259 260      virtual InputIterator do_get(261        InputIterator it, InputIterator end, std::ios_base& iosb,262        std::ios_base::iostate& state, long double& val) const263      {264        get_and_check_eof(it, end, iosb, state, val);265        return it;266      }267 268      //..............................................................................269 270    private:271      template<class ValType> static ValType positive_nan()272      {273        // On some platforms quiet_NaN() may be negative.274        return (boost::math::copysign)(275          std::numeric_limits<ValType>::quiet_NaN(), static_cast<ValType>(1)276          );277        // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11278      }279 280      template<class ValType> void get_and_check_eof281      (282        InputIterator& it, InputIterator end, std::ios_base& iosb,283        std::ios_base::iostate& state, ValType& val284      ) const285      {286        get_signed(it, end, iosb, state, val);287        if(it == end)288          state |= std::ios_base::eofbit;289      }290 291      template<class ValType> void get_signed292      (293        InputIterator& it, InputIterator end, std::ios_base& iosb,294        std::ios_base::iostate& state, ValType& val295      ) const296      {297        const std::ctype<CharType>& ct298          = std::use_facet<std::ctype<CharType> >(iosb.getloc());299 300        char c = peek_char(it, end, ct);301 302        bool negative = (c == '-');303 304        if(negative || c == '+')305        {306          ++it;307          c = peek_char(it, end, ct);308          if(c == '-' || c == '+')309          { // Without this check, "++5" etc would be accepted.310            state |= std::ios_base::failbit;311            return;312          }313        }314 315        get_unsigned(it, end, iosb, ct, state, val);316 317        if(negative)318        {319          val = (boost::math::changesign)(val);320        }321      } // void get_signed322 323      template<class ValType> void get_unsigned324      ( //! Get an unsigned floating-point value into val,325        //! but checking for letters indicating non-finites.326        InputIterator& it, InputIterator end, std::ios_base& iosb,327        const std::ctype<CharType>& ct,328        std::ios_base::iostate& state, ValType& val329      ) const330      {331        switch(peek_char(it, end, ct))332        {333        case 'i':334          get_i(it, end, ct, state, val);335          break;336 337        case 'n':338          get_n(it, end, ct, state, val);339          break;340 341        case 'q':342        case 's':343          get_q(it, end, ct, state, val);344          break;345 346        default: // Got a normal floating-point value into val.347          it = std::num_get<CharType, InputIterator>::do_get(348            it, end, iosb, state, val);349          if((flags_ & legacy) && val == static_cast<ValType>(1)350            && peek_char(it, end, ct) == '#')351            get_one_hash(it, end, ct, state, val);352          break;353        }354      } //  get_unsigned355 356      //..........................................................................357 358      template<class ValType> void get_i359      ( // Get the rest of all strings starting with 'i', expect "inf", "infinity".360        InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,361        std::ios_base::iostate& state, ValType& val362      ) const363      {364        if(!std::numeric_limits<ValType>::has_infinity365          || (flags_ & trap_infinity))366        {367            state |= std::ios_base::failbit;368            return;369        }370 371        ++it;372        if(!match_string(it, end, ct, "nf"))373        {374          state |= std::ios_base::failbit;375          return;376        }377 378        if(peek_char(it, end, ct) != 'i')379        {380          val = std::numeric_limits<ValType>::infinity();  // "inf"381          return;382        }383 384        ++it;385        if(!match_string(it, end, ct, "nity"))386        { // Expected "infinity"387          state |= std::ios_base::failbit;388          return;389        }390 391        val = std::numeric_limits<ValType>::infinity(); // "infinity"392      } // void get_i393 394      template<class ValType> void get_n395      ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)"396        InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,397        std::ios_base::iostate& state, ValType& val398      ) const399      {400        if(!std::numeric_limits<ValType>::has_quiet_NaN401          || (flags_ & trap_nan)) {402            state |= std::ios_base::failbit;403            return;404        }405 406        ++it;407        if(!match_string(it, end, ct, "an"))408        {409          state |= std::ios_base::failbit;410          return;411        }412 413        switch(peek_char(it, end, ct)) {414        case 'q':415        case 's':416          if(flags_ & legacy)417            ++it;418          break;  // "nanq", "nans"419 420        case '(':   // Optional payload field in (...) follows.421         {422            ++it;423            char c;424            while((c = peek_char(it, end, ct))425              && c != ')' && c != ' ' && c != '\n' && c != '\t')426              ++it;427            if(c != ')')428            { // Optional payload field terminator missing!429              state |= std::ios_base::failbit;430              return;431            }432            ++it;433            break;  // "nan(...)"434          }435 436        default:437          break;  // "nan"438        }439 440        val = positive_nan<ValType>();441      } // void get_n442 443      template<class ValType> void get_q444      ( // Get expected rest of string starting with 'q': "qnan".445        InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,446        std::ios_base::iostate& state, ValType& val447      ) const448      {449        if(!std::numeric_limits<ValType>::has_quiet_NaN450          || (flags_ & trap_nan) || !(flags_ & legacy))451        {452          state |= std::ios_base::failbit;453          return;454        }455 456        ++it;457        if(!match_string(it, end, ct, "nan"))458        {459          state |= std::ios_base::failbit;460          return;461        }462 463        val = positive_nan<ValType>(); // "QNAN"464      } //  void get_q465 466      template<class ValType> void get_one_hash467      ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN".468        InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,469        std::ios_base::iostate& state, ValType& val470      ) const471      {472 473        ++it;474        switch(peek_char(it, end, ct))475        {476        case 'i': // from IND (indeterminate), considered same a QNAN.477          get_one_hash_i(it, end, ct, state, val); // "1.#IND"478          return;479 480        case 'q': // from QNAN481        case 's': // from SNAN - treated the same as QNAN.482          if(std::numeric_limits<ValType>::has_quiet_NaN483            && !(flags_ & trap_nan))484          {485            ++it;486            if(match_string(it, end, ct, "nan"))487            { // "1.#QNAN", "1.#SNAN"488 //             ++it; // removed as caused assert() cannot increment iterator).489// (match_string consumes string, so not needed?).490// https://svn.boost.org/trac/boost/ticket/5467491// Change in nonfinite_num_facet.hpp Paul A. Bristow 11 Apr 11 makes legacy_test.cpp work OK.492              val = positive_nan<ValType>(); // "1.#QNAN"493              return;494            }495          }496          break;  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.497 498        default:499          break;  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.500        }501 502        state |= std::ios_base::failbit;503      } //  void get_one_hash504 505      template<class ValType> void get_one_hash_i506      ( // Get expected strings after 'i', "1.#INF", 1.#IND".507        InputIterator& it, InputIterator end, const std::ctype<CharType>& ct,508        std::ios_base::iostate& state, ValType& val509      ) const510      {511        ++it;512 513        if(peek_char(it, end, ct) == 'n')514        {515          ++it;516          switch(peek_char(it, end, ct))517          {518          case 'f':  // "1.#INF"519            if(std::numeric_limits<ValType>::has_infinity520              && !(flags_ & trap_infinity))521            {522                ++it;523                val = std::numeric_limits<ValType>::infinity();524                return;525            }526            break;  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.527 528          case 'd':   // 1.#IND"529            if(std::numeric_limits<ValType>::has_quiet_NaN530              && !(flags_ & trap_nan))531            {532                ++it;533                val = positive_nan<ValType>();534                return;535            }536            break;  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.537 538          default:  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.539            break;  // LCOV_EXCL_LINE  simple fallthrough not registered as covered.540          }541        }542 543        state |= std::ios_base::failbit;544      } //  void get_one_hash_i545 546      //..........................................................................547 548      char peek_char549      ( //! \return next char in the input buffer, ensuring lowercase (but do not 'consume' char).550        InputIterator& it, InputIterator end,551        const std::ctype<CharType>& ct552      ) const553      {554        if(it == end) return 0;555        return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive.556      }557 558      bool match_string559      ( //! Match remaining chars to expected string (case insensitive),560        //! consuming chars that match OK.561        //! \return true if matched expected string, else false.562        InputIterator& it, InputIterator end,563        const std::ctype<CharType>& ct,564        const char* s565      ) const566      {567        while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0))568        {569          ++s;570          ++it; //571        }572        return !*s;573      } // bool match_string574 575      const int flags_;576    }; //577 578    //------------------------------------------------------------------------------579 580  }   // namespace math581}   // namespace boost582 583#ifdef _MSC_VER584#   pragma warning(pop)585#endif586 587#endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP588 589