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