1 #ifndef BOOST_MATH_NONFINITE_NUM_FACETS_HPP
2 #define BOOST_MATH_NONFINITE_NUM_FACETS_HPP
4 // Copyright 2006 Johan Rade
5 // Copyright 2012 K R Walker
6 // Copyright 2011, 2012 Paul A. Bristow
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)
15 \brief non_finite_num facets for C99 standard output of infinity and NaN.
17 \details See fuller documentation at Boost.Math Facets
18 for Floating-Point Infinities and NaNs.
26 #include <boost/version.hpp>
27 #include <boost/throw_exception.hpp>
29 #include <boost/math/special_functions/fpclassify.hpp>
30 #include <boost/math/special_functions/sign.hpp>
33 # pragma warning(push)
34 # pragma warning(disable : 4127) // conditional expression is constant.
35 # pragma warning(disable : 4706) // assignment within conditional expression.
41 // flags (enums can be ORed together) -----------------------------------
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.
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.
56 // class nonfinite_num_put -----------------------------------------------------
60 class OutputIterator = std::ostreambuf_iterator<CharType>
62 class nonfinite_num_put : public std::num_put<CharType, OutputIterator>
65 explicit nonfinite_num_put(int flags = 0) : flags_(flags) {}
68 virtual OutputIterator do_put(
69 OutputIterator it, std::ios_base& iosb, CharType fill, double val) const
71 put_and_reset_width(it, iosb, fill, val);
75 virtual OutputIterator do_put(
76 OutputIterator it, std::ios_base& iosb, CharType fill, long double val) const
78 put_and_reset_width(it, iosb, fill, val);
83 template<class ValType> void put_and_reset_width(
84 OutputIterator& it, std::ios_base& iosb,
85 CharType fill, ValType val) const
87 put_impl(it, iosb, fill, val);
91 template<class ValType> void put_impl(
92 OutputIterator& it, std::ios_base& iosb,
93 CharType fill, ValType val) const
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;
101 switch((boost::math::fpclassify)(val))
105 if(flags_ & trap_infinity)
107 BOOST_THROW_EXCEPTION(std::ios_base::failure("Infinity"));
109 else if((boost::math::signbit)(val))
110 { // negative infinity.
111 put_num_and_fill(it, iosb, prefix_minus, body_inf, fill, val);
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);
118 { // just "inf" wanted.
119 put_num_and_fill(it, iosb, null_string, body_inf, fill, val);
124 if(flags_ & trap_nan)
126 BOOST_THROW_EXCEPTION(std::ios_base::failure("NaN"));
128 else if((boost::math::signbit)(val))
129 { // negative so "-nan".
130 put_num_and_fill(it, iosb, prefix_minus, body_nan, fill, val);
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);
138 put_num_and_fill(it, iosb, null_string, body_nan, fill, val);
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.
147 std::basic_ostringstream<CharType> zeros; // Needs to be CharType version.
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));
156 put_num_and_fill(it, iosb, prefix_minus, zeros.str().c_str(), fill, val);
159 { // Output the platform default for positive and negative zero.
160 put_num_and_fill(it, iosb, null_string, null_string, fill, val);
164 default: // Normal non-zero finite value.
165 it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
170 template<class ValType>
171 void put_num_and_fill(
172 OutputIterator& it, std::ios_base& iosb, const CharType* prefix,
173 const CharType* body, CharType fill, ValType val) const
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());
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);
189 { // Adjust width for prefix.
192 iosb.width( iosb.width() - prefix_length );
193 width -= prefix_length;
198 if(adjust == std::ios_base::internal)
199 { // Put fill between sign and digits.
200 put_fill(it, iosb, fill, width);
202 if(iosb.flags() & std::ios_base::uppercase)
205 *it = ct.toupper(*(body++));
213 if(adjust == std::ios_base::left)
214 put_fill(it, iosb, fill, width);
218 it = std::num_put<CharType, OutputIterator>::do_put(it, iosb, fill, val);
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)
234 // class nonfinite_num_get ------------------------------------------------------
238 class InputIterator = std::istreambuf_iterator<CharType>
240 class nonfinite_num_get : public std::num_get<CharType, InputIterator>
244 explicit nonfinite_num_get(int flags = 0) : flags_(flags)
247 protected: // float, double and long double versions of do_get.
248 virtual InputIterator do_get(
249 InputIterator it, InputIterator end, std::ios_base& iosb,
250 std::ios_base::iostate& state, float& val) const
252 get_and_check_eof(it, end, iosb, state, val);
256 virtual InputIterator do_get(
257 InputIterator it, InputIterator end, std::ios_base& iosb,
258 std::ios_base::iostate& state, double& val) const
260 get_and_check_eof(it, end, iosb, state, val);
264 virtual InputIterator do_get(
265 InputIterator it, InputIterator end, std::ios_base& iosb,
266 std::ios_base::iostate& state, long double& val) const
268 get_and_check_eof(it, end, iosb, state, val);
272 //..............................................................................
275 template<class ValType> static ValType positive_nan()
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)
281 // static_cast<ValType>(1) added Paul A. Bristow 5 Apr 11
284 template<class ValType> void get_and_check_eof
286 InputIterator& it, InputIterator end, std::ios_base& iosb,
287 std::ios_base::iostate& state, ValType& val
290 get_signed(it, end, iosb, state, val);
292 state |= std::ios_base::eofbit;
295 template<class ValType> void get_signed
297 InputIterator& it, InputIterator end, std::ios_base& iosb,
298 std::ios_base::iostate& state, ValType& val
301 const std::ctype<CharType>& ct
302 = std::use_facet<std::ctype<CharType> >(iosb.getloc());
304 char c = peek_char(it, end, ct);
306 bool negative = (c == '-');
308 if(negative || c == '+')
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;
319 get_unsigned(it, end, iosb, ct, state, val);
323 val = (boost::math::changesign)(val);
327 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
335 switch(peek_char(it, end, ct))
338 get_i(it, end, ct, state, val);
342 get_n(it, end, ct, state, val);
347 get_q(it, end, ct, state, val);
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);
360 //..........................................................................
362 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
368 if(!std::numeric_limits<ValType>::has_infinity
369 || (flags_ & trap_infinity))
371 state |= std::ios_base::failbit;
376 if(!match_string(it, end, ct, "nf"))
378 state |= std::ios_base::failbit;
382 if(peek_char(it, end, ct) != 'i')
384 val = std::numeric_limits<ValType>::infinity(); // "inf"
389 if(!match_string(it, end, ct, "nity"))
390 { // Expected "infinity"
391 state |= std::ios_base::failbit;
395 val = std::numeric_limits<ValType>::infinity(); // "infinity"
398 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
404 if(!std::numeric_limits<ValType>::has_quiet_NaN
405 || (flags_ & trap_nan)) {
406 state |= std::ios_base::failbit;
411 if(!match_string(it, end, ct, "an"))
413 state |= std::ios_base::failbit;
417 switch(peek_char(it, end, ct)) {
422 break; // "nanq", "nans"
424 case '(': // Optional payload field in (...) follows.
428 while((c = peek_char(it, end, ct))
429 && c != ')' && c != ' ' && c != '\n' && c != '\t')
432 { // Optional payload field terminator missing!
433 state |= std::ios_base::failbit;
444 val = positive_nan<ValType>();
447 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
453 if(!std::numeric_limits<ValType>::has_quiet_NaN
454 || (flags_ & trap_nan) || !(flags_ & legacy))
456 state |= std::ios_base::failbit;
461 if(!match_string(it, end, ct, "nan"))
463 state |= std::ios_base::failbit;
467 val = positive_nan<ValType>(); // "QNAN"
470 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
478 switch(peek_char(it, end, ct))
480 case 'i': // from IND (indeterminate), considered same a QNAN.
481 get_one_hash_i(it, end, ct, state, val); // "1.#IND"
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))
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"
506 state |= std::ios_base::failbit;
507 } // void get_one_hash
509 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
517 if(peek_char(it, end, ct) == 'n')
520 switch(peek_char(it, end, ct))
522 case 'f': // "1.#INF"
523 if(std::numeric_limits<ValType>::has_infinity
524 && !(flags_ & trap_infinity))
527 val = std::numeric_limits<ValType>::infinity();
533 if(std::numeric_limits<ValType>::has_quiet_NaN
534 && !(flags_ & trap_nan))
537 val = positive_nan<ValType>();
547 state |= std::ios_base::failbit;
548 } // void get_one_hash_i
550 //..........................................................................
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
558 if(it == end) return 0;
559 return ct.narrow(ct.tolower(*it), 0); // Always tolower to ensure case insensitive.
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,
571 while(it != end && *s && *s == ct.narrow(ct.tolower(*it), 0))
577 } // bool match_string
583 //------------------------------------------------------------------------------
589 # pragma warning(pop)
592 #endif // BOOST_MATH_NONFINITE_NUM_FACETS_HPP