• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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