]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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: | |
65 | explicit nonfinite_num_put(int flags = 0) : flags_(flags) {} | |
66 | ||
67 | protected: | |
68 | 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 | ||
75 | 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: | |
83 | 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 | ||
91 | 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> | |
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 | |
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 | ||
222 | 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: | |
244 | explicit nonfinite_num_get(int flags = 0) : flags_(flags) | |
245 | {} | |
246 | ||
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 | |
251 | { | |
252 | get_and_check_eof(it, end, iosb, state, val); | |
253 | return it; | |
254 | } | |
255 | ||
256 | 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 | ||
264 | 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: | |
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 | ||
284 | 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 | ||
295 | 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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
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 | |
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 | ||
552 | 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 | ||
562 | 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 |