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 26 #include <boost/version.hpp> 27 #include <boost/throw_exception.hpp> 28 29 #include <boost/math/special_functions/fpclassify.hpp> 30 #include <boost/math/special_functions/sign.hpp> 31 32 #ifdef _MSC_VER 33 # pragma warning(push) 34 # pragma warning(disable : 4127) // conditional expression is constant. 35 # pragma warning(disable : 4706) // assignment within conditional expression. 36 #endif 37 38 namespace boost { 39 namespace math { 40 41 // flags (enums can be ORed together) ----------------------------------- 42 43 const int legacy = 0x1; //!< get facet will recognize most string representations of infinity and NaN. 44 const int signed_zero = 0x2; //!< put facet will distinguish between positive and negative zero. 45 const int trap_infinity = 0x4; /*!< put facet will throw an exception of type std::ios_base::failure 46 when an attempt is made to format positive or negative infinity. 47 get will set the fail bit of the stream when an attempt is made 48 to parse a string that represents positive or negative sign infinity. 49 */ 50 const int trap_nan = 0x8; /*!< put facet will throw an exception of type std::ios_base::failure 51 when an attempt is made to format positive or negative NaN. 52 get will set the fail bit of the stream when an attempt is made 53 to parse a string that represents positive or negative sign infinity. 54 */ 55 56 // class nonfinite_num_put ----------------------------------------------------- 57 58 template< 59 class CharType, 60 class OutputIterator = std::ostreambuf_iterator<CharType> 61 > 62 class nonfinite_num_put : public std::num_put<CharType, OutputIterator> 63 { 64 public: nonfinite_num_put(int flags=0)65 explicit nonfinite_num_put(int flags = 0) : flags_(flags) {} 66 67 protected: do_put(OutputIterator it,std::ios_base & iosb,CharType fill,double val) const68 virtual OutputIterator do_put( 69 OutputIterator it, std::ios_base& iosb, CharType fill, double val) const 70 { 71 put_and_reset_width(it, iosb, fill, val); 72 return it; 73 } 74 do_put(OutputIterator it,std::ios_base & iosb,CharType fill,long double val) const75 virtual OutputIterator do_put( 76 OutputIterator it, std::ios_base& iosb, CharType fill, long double val) const 77 { 78 put_and_reset_width(it, iosb, fill, val); 79 return it; 80 } 81 82 private: put_and_reset_width(OutputIterator & it,std::ios_base & iosb,CharType fill,ValType val) const83 template<class ValType> void put_and_reset_width( 84 OutputIterator& it, std::ios_base& iosb, 85 CharType fill, ValType val) const 86 { 87 put_impl(it, iosb, fill, val); 88 iosb.width(0); 89 } 90 put_impl(OutputIterator & it,std::ios_base & iosb,CharType fill,ValType val) const91 template<class ValType> void put_impl( 92 OutputIterator& it, std::ios_base& iosb, 93 CharType fill, ValType val) const 94 { 95 static const CharType prefix_plus[2] = { '+', '\0' }; 96 static const CharType prefix_minus[2] = { '-', '\0' }; 97 static const CharType body_inf[4] = { 'i', 'n', 'f', '\0' }; 98 static const CharType body_nan[4] = { 'n', 'a', 'n', '\0' }; 99 static const CharType* null_string = 0; 100 101 switch((boost::math::fpclassify)(val)) 102 { 103 104 case FP_INFINITE: 105 if(flags_ & trap_infinity) 106 { 107 BOOST_THROW_EXCEPTION(std::ios_base::failure("Infinity")); 108 } 109 else if((boost::math::signbit)(val)) 110 { // negative infinity. 111 put_num_and_fill(it, iosb, prefix_minus, body_inf, fill, val); 112 } 113 else if(iosb.flags() & std::ios_base::showpos) 114 { // Explicit "+inf" wanted. 115 put_num_and_fill(it, iosb, prefix_plus, body_inf, fill, val); 116 } 117 else 118 { // just "inf" wanted. 119 put_num_and_fill(it, iosb, null_string, body_inf, fill, val); 120 } 121 break; 122 123 case FP_NAN: 124 if(flags_ & trap_nan) 125 { 126 BOOST_THROW_EXCEPTION(std::ios_base::failure("NaN")); 127 } 128 else if((boost::math::signbit)(val)) 129 { // negative so "-nan". 130 put_num_and_fill(it, iosb, prefix_minus, body_nan, fill, val); 131 } 132 else if(iosb.flags() & std::ios_base::showpos) 133 { // explicit "+nan" wanted. 134 put_num_and_fill(it, iosb, prefix_plus, body_nan, fill, val); 135 } 136 else 137 { // Just "nan". 138 put_num_and_fill(it, iosb, null_string, body_nan, fill, val); 139 } 140 break; 141 142 case FP_ZERO: 143 if((flags_ & signed_zero) && ((boost::math::signbit)(val))) 144 { // Flag set to distinguish between positive and negative zero. 145 // But string "0" should have stuff after decimal point if setprecision and/or exp format. 146 147 std::basic_ostringstream<CharType> zeros; // Needs to be CharType version. 148 149 // Copy flags, fill, width and precision. 150 zeros.flags(iosb.flags()); 151 zeros.unsetf(std::ios::showpos); // Ignore showpos because must be negative. 152 zeros.precision(iosb.precision()); 153 //zeros.width is set by put_num_and_fill 154 zeros.fill(static_cast<char>(fill)); 155 zeros << ValType(0); 156 put_num_and_fill(it, iosb, prefix_minus, zeros.str().c_str(), fill, val); 157 } 158 else 159 { // Output the platform default for positive and negative zero. 160 put_num_and_fill(it, iosb, null_string, null_string, fill, val); 161 } 162 break; 163 164 default: // Normal non-zero finite value. 165 it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val); 166 break; 167 } 168 } 169 170 template<class ValType> put_num_and_fill(OutputIterator & it,std::ios_base & iosb,const CharType * prefix,const CharType * body,CharType fill,ValType val) const171 void put_num_and_fill( 172 OutputIterator& it, std::ios_base& iosb, const CharType* prefix, 173 const CharType* body, CharType fill, ValType val) const 174 { 175 int prefix_length = prefix ? (int)std::char_traits<CharType>::length(prefix) : 0; 176 int body_length = body ? (int)std::char_traits<CharType>::length(body) : 0; 177 int width = prefix_length + body_length; 178 std::ios_base::fmtflags adjust = iosb.flags() & std::ios_base::adjustfield; 179 const std::ctype<CharType>& ct 180 = std::use_facet<std::ctype<CharType> >(iosb.getloc()); 181 182 if(body || prefix) 183 { // adjust == std::ios_base::right, so leading fill needed. 184 if(adjust != std::ios_base::internal && adjust != std::ios_base::left) 185 put_fill(it, iosb, fill, width); 186 } 187 188 if(prefix) 189 { // Adjust width for prefix. 190 while(*prefix) 191 *it = *(prefix++); 192 iosb.width( iosb.width() - prefix_length ); 193 width -= prefix_length; 194 } 195 196 if(body) 197 { // 198 if(adjust == std::ios_base::internal) 199 { // Put fill between sign and digits. 200 put_fill(it, iosb, fill, width); 201 } 202 if(iosb.flags() & std::ios_base::uppercase) 203 { 204 while(*body) 205 *it = ct.toupper(*(body++)); 206 } 207 else 208 { 209 while(*body) 210 *it = *(body++); 211 } 212 213 if(adjust == std::ios_base::left) 214 put_fill(it, iosb, fill, width); 215 } 216 else 217 { 218 it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val); 219 } 220 } 221 put_fill(OutputIterator & it,std::ios_base & iosb,CharType fill,int width) const222 void put_fill( 223 OutputIterator& it, std::ios_base& iosb, CharType fill, int width) const 224 { // Insert fill chars. 225 for(std::streamsize i = iosb.width() - static_cast<std::streamsize>(width); i > 0; --i) 226 *it = fill; 227 } 228 229 private: 230 const int flags_; 231 }; 232 233 234 // class nonfinite_num_get ------------------------------------------------------ 235 236 template< 237 class CharType, 238 class InputIterator = std::istreambuf_iterator<CharType> 239 > 240 class nonfinite_num_get : public std::num_get<CharType, InputIterator> 241 { 242 243 public: nonfinite_num_get(int flags=0)244 explicit nonfinite_num_get(int flags = 0) : flags_(flags) 245 {} 246 247 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) const248 virtual InputIterator do_get( 249 InputIterator it, InputIterator end, std::ios_base& iosb, 250 std::ios_base::iostate& state, float& val) const 251 { 252 get_and_check_eof(it, end, iosb, state, val); 253 return it; 254 } 255 do_get(InputIterator it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,double & val) const256 virtual InputIterator do_get( 257 InputIterator it, InputIterator end, std::ios_base& iosb, 258 std::ios_base::iostate& state, double& val) const 259 { 260 get_and_check_eof(it, end, iosb, state, val); 261 return it; 262 } 263 do_get(InputIterator it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,long double & val) const264 virtual InputIterator do_get( 265 InputIterator it, InputIterator end, std::ios_base& iosb, 266 std::ios_base::iostate& state, long double& val) const 267 { 268 get_and_check_eof(it, end, iosb, state, val); 269 return it; 270 } 271 272 //.............................................................................. 273 274 private: positive_nan()275 template<class ValType> static ValType positive_nan() 276 { 277 // On some platforms quiet_NaN() may be negative. 278 return (boost::math::copysign)( 279 std::numeric_limits<ValType>::quiet_NaN(), static_cast<ValType>(1) 280 ); 281 // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11 282 } 283 get_and_check_eof(InputIterator & it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,ValType & val) const284 template<class ValType> void get_and_check_eof 285 ( 286 InputIterator& it, InputIterator end, std::ios_base& iosb, 287 std::ios_base::iostate& state, ValType& val 288 ) const 289 { 290 get_signed(it, end, iosb, state, val); 291 if(it == end) 292 state |= std::ios_base::eofbit; 293 } 294 get_signed(InputIterator & it,InputIterator end,std::ios_base & iosb,std::ios_base::iostate & state,ValType & val) const295 template<class ValType> void get_signed 296 ( 297 InputIterator& it, InputIterator end, std::ios_base& iosb, 298 std::ios_base::iostate& state, ValType& val 299 ) const 300 { 301 const std::ctype<CharType>& ct 302 = std::use_facet<std::ctype<CharType> >(iosb.getloc()); 303 304 char c = peek_char(it, end, ct); 305 306 bool negative = (c == '-'); 307 308 if(negative || c == '+') 309 { 310 ++it; 311 c = peek_char(it, end, ct); 312 if(c == '-' || c == '+') 313 { // Without this check, "++5" etc would be accepted. 314 state |= std::ios_base::failbit; 315 return; 316 } 317 } 318 319 get_unsigned(it, end, iosb, ct, state, val); 320 321 if(negative) 322 { 323 val = (boost::math::changesign)(val); 324 } 325 } // void get_signed 326 get_unsigned(InputIterator & it,InputIterator end,std::ios_base & iosb,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const327 template<class ValType> void get_unsigned 328 ( //! Get an unsigned floating-point value into val, 329 //! but checking for letters indicating non-finites. 330 InputIterator& it, InputIterator end, std::ios_base& iosb, 331 const std::ctype<CharType>& ct, 332 std::ios_base::iostate& state, ValType& val 333 ) const 334 { 335 switch(peek_char(it, end, ct)) 336 { 337 case 'i': 338 get_i(it, end, ct, state, val); 339 break; 340 341 case 'n': 342 get_n(it, end, ct, state, val); 343 break; 344 345 case 'q': 346 case 's': 347 get_q(it, end, ct, state, val); 348 break; 349 350 default: // Got a normal floating-point value into val. 351 it = std::num_get<CharType, InputIterator>::do_get( 352 it, end, iosb, state, val); 353 if((flags_ & legacy) && val == static_cast<ValType>(1) 354 && peek_char(it, end, ct) == '#') 355 get_one_hash(it, end, ct, state, val); 356 break; 357 } 358 } // get_unsigned 359 360 //.......................................................................... 361 get_i(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const362 template<class ValType> void get_i 363 ( // Get the rest of all strings starting with 'i', expect "inf", "infinity". 364 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 365 std::ios_base::iostate& state, ValType& val 366 ) const 367 { 368 if(!std::numeric_limits<ValType>::has_infinity 369 || (flags_ & trap_infinity)) 370 { 371 state |= std::ios_base::failbit; 372 return; 373 } 374 375 ++it; 376 if(!match_string(it, end, ct, "nf")) 377 { 378 state |= std::ios_base::failbit; 379 return; 380 } 381 382 if(peek_char(it, end, ct) != 'i') 383 { 384 val = std::numeric_limits<ValType>::infinity(); // "inf" 385 return; 386 } 387 388 ++it; 389 if(!match_string(it, end, ct, "nity")) 390 { // Expected "infinity" 391 state |= std::ios_base::failbit; 392 return; 393 } 394 395 val = std::numeric_limits<ValType>::infinity(); // "infinity" 396 } // void get_i 397 get_n(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const398 template<class ValType> void get_n 399 ( // Get expected strings after 'n', "nan", "nanq", "nans", "nan(...)" 400 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 401 std::ios_base::iostate& state, ValType& val 402 ) const 403 { 404 if(!std::numeric_limits<ValType>::has_quiet_NaN 405 || (flags_ & trap_nan)) { 406 state |= std::ios_base::failbit; 407 return; 408 } 409 410 ++it; 411 if(!match_string(it, end, ct, "an")) 412 { 413 state |= std::ios_base::failbit; 414 return; 415 } 416 417 switch(peek_char(it, end, ct)) { 418 case 'q': 419 case 's': 420 if(flags_ && legacy) 421 ++it; 422 break; // "nanq", "nans" 423 424 case '(': // Optional payload field in (...) follows. 425 { 426 ++it; 427 char c; 428 while((c = peek_char(it, end, ct)) 429 && c != ')' && c != ' ' && c != '\n' && c != '\t') 430 ++it; 431 if(c != ')') 432 { // Optional payload field terminator missing! 433 state |= std::ios_base::failbit; 434 return; 435 } 436 ++it; 437 break; // "nan(...)" 438 } 439 440 default: 441 break; // "nan" 442 } 443 444 val = positive_nan<ValType>(); 445 } // void get_n 446 get_q(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const447 template<class ValType> void get_q 448 ( // Get expected rest of string starting with 'q': "qnan". 449 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 450 std::ios_base::iostate& state, ValType& val 451 ) const 452 { 453 if(!std::numeric_limits<ValType>::has_quiet_NaN 454 || (flags_ & trap_nan) || !(flags_ & legacy)) 455 { 456 state |= std::ios_base::failbit; 457 return; 458 } 459 460 ++it; 461 if(!match_string(it, end, ct, "nan")) 462 { 463 state |= std::ios_base::failbit; 464 return; 465 } 466 467 val = positive_nan<ValType>(); // "QNAN" 468 } // void get_q 469 get_one_hash(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const470 template<class ValType> void get_one_hash 471 ( // Get expected string after having read "1.#": "1.#IND", "1.#QNAN", "1.#SNAN". 472 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 473 std::ios_base::iostate& state, ValType& val 474 ) const 475 { 476 477 ++it; 478 switch(peek_char(it, end, ct)) 479 { 480 case 'i': // from IND (indeterminate), considered same a QNAN. 481 get_one_hash_i(it, end, ct, state, val); // "1.#IND" 482 return; 483 484 case 'q': // from QNAN 485 case 's': // from SNAN - treated the same as QNAN. 486 if(std::numeric_limits<ValType>::has_quiet_NaN 487 && !(flags_ & trap_nan)) 488 { 489 ++it; 490 if(match_string(it, end, ct, "nan")) 491 { // "1.#QNAN", "1.#SNAN" 492 // ++it; // removed as caused assert() cannot increment iterator). 493 // (match_string consumes string, so not needed?). 494 // https://svn.boost.org/trac/boost/ticket/5467 495 // Change in nonfinite_num_facet.hpp Paul A. Bristow 11 Apr 11 makes legacy_test.cpp work OK. 496 val = positive_nan<ValType>(); // "1.#QNAN" 497 return; 498 } 499 } 500 break; 501 502 default: 503 break; 504 } 505 506 state |= std::ios_base::failbit; 507 } // void get_one_hash 508 get_one_hash_i(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,std::ios_base::iostate & state,ValType & val) const509 template<class ValType> void get_one_hash_i 510 ( // Get expected strings after 'i', "1.#INF", 1.#IND". 511 InputIterator& it, InputIterator end, const std::ctype<CharType>& ct, 512 std::ios_base::iostate& state, ValType& val 513 ) const 514 { 515 ++it; 516 517 if(peek_char(it, end, ct) == 'n') 518 { 519 ++it; 520 switch(peek_char(it, end, ct)) 521 { 522 case 'f': // "1.#INF" 523 if(std::numeric_limits<ValType>::has_infinity 524 && !(flags_ & trap_infinity)) 525 { 526 ++it; 527 val = std::numeric_limits<ValType>::infinity(); 528 return; 529 } 530 break; 531 532 case 'd': // 1.#IND" 533 if(std::numeric_limits<ValType>::has_quiet_NaN 534 && !(flags_ & trap_nan)) 535 { 536 ++it; 537 val = positive_nan<ValType>(); 538 return; 539 } 540 break; 541 542 default: 543 break; 544 } 545 } 546 547 state |= std::ios_base::failbit; 548 } // void get_one_hash_i 549 550 //.......................................................................... 551 peek_char(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct) const552 char peek_char 553 ( //! \return next char in the input buffer, ensuring lowercase (but do not 'consume' char). 554 InputIterator& it, InputIterator end, 555 const std::ctype<CharType>& ct 556 ) const 557 { 558 if(it == end) return 0; 559 return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive. 560 } 561 match_string(InputIterator & it,InputIterator end,const std::ctype<CharType> & ct,const char * s) const562 bool match_string 563 ( //! Match remaining chars to expected string (case insensitive), 564 //! consuming chars that match OK. 565 //! \return true if matched expected string, else false. 566 InputIterator& it, InputIterator end, 567 const std::ctype<CharType>& ct, 568 const char* s 569 ) const 570 { 571 while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0)) 572 { 573 ++s; 574 ++it; // 575 } 576 return !*s; 577 } // bool match_string 578 579 private: 580 const int flags_; 581 }; // 582 583 //------------------------------------------------------------------------------ 584 585 } // namespace math 586 } // namespace boost 587 588 #ifdef _MSC_VER 589 # pragma warning(pop) 590 #endif 591 592 #endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP 593 594