1 #ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP 2 #define BOOST_MATH_NONFINITE_NUM_FACETS_HPP 3 4 // Copyright 2006 Johan Rade 5 // Copyright 2012 K R Walker 6 // Copyright 2011, 2012 Paul A. Bristow 7 8 // Distributed under the Boost Software License, Version 1.0. 9 // (See accompanying file LICENSE_1_0.txt 10 // or copy at http://www.boost.org/LICENSE_1_0.txt) 11 12 /* 13 \file 14 15 \brief non_finite_num facets for C99 standard output of infinity and NaN. 16 17 \details See fuller documentation at Boost.Math Facets 18 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_VER 30 # pragma warning(push) 31 # pragma warning(disable : 4127) // conditional expression is constant. 32 # pragma warning(disable : 4706) // assignment within conditional expression. 33 #endif 34 35 namespace 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::failure 43 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 made 45 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::failure 48 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 made 50 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: nonfinite_num_put(int flags=0)62 explicit nonfinite_num_put(int flags = 0) : flags_(flags) {} 63 64 protected: do_put(OutputIterator it,std::ios_base & iosb,CharType fill,double val) const65 virtual OutputIterator do_put( 66 OutputIterator it, std::ios_base& iosb, CharType fill, double val) const 67 { 68 put_and_reset_width(it, iosb, fill, val); 69 return it; 70 } 71 do_put(OutputIterator it,std::ios_base & iosb,CharType fill,long double val) const72 virtual OutputIterator do_put( 73 OutputIterator it, std::ios_base& iosb, CharType fill, long double val) const 74 { 75 put_and_reset_width(it, iosb, fill, val); 76 return it; 77 } 78 79 private: put_and_reset_width(OutputIterator & it,std::ios_base & iosb,CharType fill,ValType val) const80 template<class ValType> void put_and_reset_width( 81 OutputIterator& it, std::ios_base& iosb, 82 CharType fill, ValType val) const 83 { 84 put_impl(it, iosb, fill, val); 85 iosb.width(0); 86 } 87 put_impl(OutputIterator & it,std::ios_base & iosb,CharType fill,ValType val) const88 template<class ValType> void put_impl( 89 OutputIterator& it, std::ios_base& iosb, 90 CharType fill, ValType val) const 91 { 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 else 115 { // 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 else 134 { // 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_fill 151 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 else 156 { // 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> put_num_and_fill(OutputIterator & it,std::ios_base & iosb,const CharType * prefix,const CharType * body,CharType fill,ValType val) const168 void put_num_and_fill( 169 OutputIterator& it, std::ios_base& iosb, const CharType* prefix, 170 const CharType* body, CharType fill, ValType val) const 171 { 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>& ct 177 = 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 else 205 { 206 while(*body) 207 *it = *(body++); 208 } 209 210 if(adjust == std::ios_base::left) 211 put_fill(it, iosb, fill, width); 212 } 213 else 214 { 215 it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val); 216 } 217 } 218 put_fill(OutputIterator & it,std::ios_base & iosb,CharType fill,int width) const219 void put_fill( 220 OutputIterator& it, std::ios_base& iosb, CharType fill, int width) const 221 { // Insert fill chars. 222 for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i) 223 *it = fill; 224 } 225 226 private: 227 const int flags_; 228 }; 229 230 231 // class nonfinite_num_get ------------------------------------------------------ 232 233 template< 234 class CharType, 235 class InputIterator = std::istreambuf_iterator<CharType> 236 > 237 class nonfinite_num_get : public std::num_get<CharType, InputIterator> 238 { 239 240 public: nonfinite_num_get(int flags=0)241 explicit nonfinite_num_get(int flags = 0) : flags_(flags) 242 {} 243 244 protected: // float, double and long double versions of do_get. do_get(InputIterator it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,float & val) const245 virtual InputIterator do_get( 246 InputIterator it, InputIterator end, std::ios_base& iosb, 247 std::ios_base::iostate& state, float& val) const 248 { 249 get_and_check_eof(it, end, iosb, state, val); 250 return it; 251 } 252 do_get(InputIterator it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,double & val) const253 virtual InputIterator do_get( 254 InputIterator it, InputIterator end, std::ios_base& iosb, 255 std::ios_base::iostate& state, double& val) const 256 { 257 get_and_check_eof(it, end, iosb, state, val); 258 return it; 259 } 260 do_get(InputIterator it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,long double & val) const261 virtual InputIterator do_get( 262 InputIterator it, InputIterator end, std::ios_base& iosb, 263 std::ios_base::iostate& state, long double& val) const 264 { 265 get_and_check_eof(it, end, iosb, state, val); 266 return it; 267 } 268 269 //.............................................................................. 270 271 private: positive_nan()272 template<class ValType> static ValType positive_nan() 273 { 274 // On some platforms quiet_NaN() may be negative. 275 return (boost::math::copysign)( 276 std::numeric_limits<ValType>::quiet_NaN(), static_cast<ValType>(1) 277 ); 278 // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11 279 } 280 get_and_check_eof(InputIterator & it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,ValType & val) const281 template<class ValType> void get_and_check_eof 282 ( 283 InputIterator& it, InputIterator end, std::ios_base& iosb, 284 std::ios_base::iostate& state, ValType& val 285 ) const 286 { 287 get_signed(it, end, iosb, state, val); 288 if(it == end) 289 state |= std::ios_base::eofbit; 290 } 291 get_signed(InputIterator & it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,ValType & val) const292 template<class ValType> void get_signed 293 ( 294 InputIterator& it, InputIterator end, std::ios_base& iosb, 295 std::ios_base::iostate& state, ValType& val 296 ) const 297 { 298 const std::ctype<CharType>& ct 299 = std::use_facet<std::ctype<CharType> >(iosb.getloc()); 300 301 char c = peek_char(it, end, ct); 302 303 bool negative = (c == '-'); 304 305 if(negative || c == '+') 306 { 307 ++it; 308 c = peek_char(it, end, ct); 309 if(c == '-' || c == '+') 310 { // Without this check, "++5" etc would be accepted. 311 state |= std::ios_base::failbit; 312 return; 313 } 314 } 315 316 get_unsigned(it, end, iosb, ct, state, val); 317 318 if(negative) 319 { 320 val = (boost::math::changesign)(val); 321 } 322 } // void get_signed 323 get_unsigned(InputIterator & it,InputIterator end,std::ios_base & iosb,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const324 template<class ValType> void get_unsigned 325 ( //! Get an unsigned floating-point value into val, 326 //! but checking for letters indicating non-finites. 327 InputIterator& it, InputIterator end, std::ios_base& iosb, 328 const std::ctype<CharType>& ct, 329 std::ios_base::iostate& state, ValType& val 330 ) const 331 { 332 switch(peek_char(it, end, ct)) 333 { 334 case 'i': 335 get_i(it, end, ct, state, val); 336 break; 337 338 case 'n': 339 get_n(it, end, ct, state, val); 340 break; 341 342 case 'q': 343 case 's': 344 get_q(it, end, ct, state, val); 345 break; 346 347 default: // Got a normal floating-point value into val. 348 it = std::num_get<CharType, InputIterator>::do_get( 349 it, end, iosb, state, val); 350 if((flags_ & legacy) && val == static_cast<ValType>(1) 351 && peek_char(it, end, ct) == '#') 352 get_one_hash(it, end, ct, state, val); 353 break; 354 } 355 } // get_unsigned 356 357 //.......................................................................... 358 get_i(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const359 template<class ValType> void get_i 360 ( // Get the rest of all strings starting with 'i', expect "inf", "infinity". 361 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 362 std::ios_base::iostate& state, ValType& val 363 ) const 364 { 365 if(!std::numeric_limits<ValType>::has_infinity 366 || (flags_ & trap_infinity)) 367 { 368 state |= std::ios_base::failbit; 369 return; 370 } 371 372 ++it; 373 if(!match_string(it, end, ct, "nf")) 374 { 375 state |= std::ios_base::failbit; 376 return; 377 } 378 379 if(peek_char(it, end, ct) != 'i') 380 { 381 val = std::numeric_limits<ValType>::infinity(); // "inf" 382 return; 383 } 384 385 ++it; 386 if(!match_string(it, end, ct, "nity")) 387 { // Expected "infinity" 388 state |= std::ios_base::failbit; 389 return; 390 } 391 392 val = std::numeric_limits<ValType>::infinity(); // "infinity" 393 } // void get_i 394 get_n(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const395 template<class ValType> void get_n 396 ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)" 397 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 398 std::ios_base::iostate& state, ValType& val 399 ) const 400 { 401 if(!std::numeric_limits<ValType>::has_quiet_NaN 402 || (flags_ & trap_nan)) { 403 state |= std::ios_base::failbit; 404 return; 405 } 406 407 ++it; 408 if(!match_string(it, end, ct, "an")) 409 { 410 state |= std::ios_base::failbit; 411 return; 412 } 413 414 switch(peek_char(it, end, ct)) { 415 case 'q': 416 case 's': 417 if(flags_ & legacy) 418 ++it; 419 break; // "nanq", "nans" 420 421 case '(': // Optional payload field in (...) follows. 422 { 423 ++it; 424 char c; 425 while((c = peek_char(it, end, ct)) 426 && c != ')' && c != ' ' && c != '\n' && c != '\t') 427 ++it; 428 if(c != ')') 429 { // Optional payload field terminator missing! 430 state |= std::ios_base::failbit; 431 return; 432 } 433 ++it; 434 break; // "nan(...)" 435 } 436 437 default: 438 break; // "nan" 439 } 440 441 val = positive_nan<ValType>(); 442 } // void get_n 443 get_q(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const444 template<class ValType> void get_q 445 ( // Get expected rest of string starting with 'q': "qnan". 446 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 447 std::ios_base::iostate& state, ValType& val 448 ) const 449 { 450 if(!std::numeric_limits<ValType>::has_quiet_NaN 451 || (flags_ & trap_nan) || !(flags_ & legacy)) 452 { 453 state |= std::ios_base::failbit; 454 return; 455 } 456 457 ++it; 458 if(!match_string(it, end, ct, "nan")) 459 { 460 state |= std::ios_base::failbit; 461 return; 462 } 463 464 val = positive_nan<ValType>(); // "QNAN" 465 } // void get_q 466 get_one_hash(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const467 template<class ValType> void get_one_hash 468 ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN". 469 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 470 std::ios_base::iostate& state, ValType& val 471 ) const 472 { 473 474 ++it; 475 switch(peek_char(it, end, ct)) 476 { 477 case 'i': // from IND (indeterminate), considered same a QNAN. 478 get_one_hash_i(it, end, ct, state, val); // "1.#IND" 479 return; 480 481 case 'q': // from QNAN 482 case 's': // from SNAN - treated the same as QNAN. 483 if(std::numeric_limits<ValType>::has_quiet_NaN 484 && !(flags_ & trap_nan)) 485 { 486 ++it; 487 if(match_string(it, end, ct, "nan")) 488 { // "1.#QNAN", "1.#SNAN" 489 // ++it; // removed as caused assert() cannot increment iterator). 490 // (match_string consumes string, so not needed?). 491 // https://svn.boost.org/trac/boost/ticket/5467 492 // Change in nonfinite_num_facet.hpp Paul A. Bristow 11 Apr 11 makes legacy_test.cpp work OK. 493 val = positive_nan<ValType>(); // "1.#QNAN" 494 return; 495 } 496 } 497 break; 498 499 default: 500 break; 501 } 502 503 state |= std::ios_base::failbit; 504 } // void get_one_hash 505 get_one_hash_i(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const506 template<class ValType> void get_one_hash_i 507 ( // Get expected strings after 'i', "1.#INF", 1.#IND". 508 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 509 std::ios_base::iostate& state, ValType& val 510 ) const 511 { 512 ++it; 513 514 if(peek_char(it, end, ct) == 'n') 515 { 516 ++it; 517 switch(peek_char(it, end, ct)) 518 { 519 case 'f': // "1.#INF" 520 if(std::numeric_limits<ValType>::has_infinity 521 && !(flags_ & trap_infinity)) 522 { 523 ++it; 524 val = std::numeric_limits<ValType>::infinity(); 525 return; 526 } 527 break; 528 529 case 'd': // 1.#IND" 530 if(std::numeric_limits<ValType>::has_quiet_NaN 531 && !(flags_ & trap_nan)) 532 { 533 ++it; 534 val = positive_nan<ValType>(); 535 return; 536 } 537 break; 538 539 default: 540 break; 541 } 542 } 543 544 state |= std::ios_base::failbit; 545 } // void get_one_hash_i 546 547 //.......................................................................... 548 peek_char(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct) const549 char peek_char 550 ( //! \return next char in the input buffer, ensuring lowercase (but do not 'consume' char). 551 InputIterator& it, InputIterator end, 552 const std::ctype<CharType>& ct 553 ) const 554 { 555 if(it == end) return 0; 556 return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive. 557 } 558 match_string(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,const char * s) const559 bool match_string 560 ( //! Match remaining chars to expected string (case insensitive), 561 //! consuming chars that match OK. 562 //! \return true if matched expected string, else false. 563 InputIterator& it, InputIterator end, 564 const std::ctype<CharType>& ct, 565 const char* s 566 ) const 567 { 568 while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0)) 569 { 570 ++s; 571 ++it; // 572 } 573 return !*s; 574 } // bool match_string 575 576 private: 577 const int flags_; 578 }; // 579 580 //------------------------------------------------------------------------------ 581 582 } // namespace math 583 } // namespace boost 584 585 #ifdef _MSC_VER 586 # pragma warning(pop) 587 #endif 588 589 #endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP 590 591