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