1 // Copyright (c) 2001-2020 Hartmut Kaiser 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #if !defined(BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM) 7 #define BOOST_SPIRIT_KARMA_REAL_UTILS_FEB_23_2007_0841PM 8 9 #if defined(_MSC_VER) 10 #pragma once 11 #endif 12 13 #include <boost/config.hpp> 14 #include <boost/config/no_tr1/cmath.hpp> 15 #include <boost/detail/workaround.hpp> 16 #include <boost/limits.hpp> 17 18 #include <boost/spirit/home/support/char_class.hpp> 19 #include <boost/spirit/home/support/unused.hpp> 20 #include <boost/spirit/home/support/detail/pow10.hpp> 21 #include <boost/spirit/home/karma/detail/generate_to.hpp> 22 #include <boost/spirit/home/karma/detail/string_generate.hpp> 23 #include <boost/spirit/home/karma/numeric/detail/numeric_utils.hpp> 24 25 namespace boost { namespace spirit { namespace karma 26 { 27 /////////////////////////////////////////////////////////////////////////// 28 // 29 // The real_inserter template takes care of the floating point number to 30 // string conversion. The Policies template parameter is used to allow 31 // customization of the formatting process 32 // 33 /////////////////////////////////////////////////////////////////////////// 34 template <typename T> 35 struct real_policies; 36 37 template <typename T 38 , typename Policies = real_policies<T> 39 , typename CharEncoding = unused_type 40 , typename Tag = unused_type> 41 struct real_inserter 42 { 43 template <typename OutputIterator, typename U> 44 static bool callboost::spirit::karma::real_inserter45 call (OutputIterator& sink, U n, Policies const& p = Policies()) 46 { 47 if (traits::test_nan(n)) { 48 return p.template nan<CharEncoding, Tag>( 49 sink, n, p.force_sign(n)); 50 } 51 else if (traits::test_infinite(n)) { 52 return p.template inf<CharEncoding, Tag>( 53 sink, n, p.force_sign(n)); 54 } 55 return p.template call<real_inserter>(sink, n, p); 56 } 57 58 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 59 # pragma warning(push) 60 # pragma warning(disable: 4100) // 'p': unreferenced formal parameter 61 # pragma warning(disable: 4127) // conditional expression is constant 62 # pragma warning(disable: 4267) // conversion from 'size_t' to 'unsigned int', possible loss of data 63 #endif 64 /////////////////////////////////////////////////////////////////////// 65 // This is the workhorse behind the real generator 66 /////////////////////////////////////////////////////////////////////// 67 template <typename OutputIterator, typename U> 68 static bool call_nboost::spirit::karma::real_inserter69 call_n (OutputIterator& sink, U n, Policies const& p) 70 { 71 // prepare sign and get output format 72 bool force_sign = p.force_sign(n); 73 bool sign_val = false; 74 int flags = p.floatfield(n); 75 if (traits::test_negative(n)) 76 { 77 n = -n; 78 sign_val = true; 79 } 80 81 // The scientific representation requires the normalization of the 82 // value to convert. 83 84 // get correct precision for generated number 85 unsigned precision = p.precision(n); 86 87 // allow for ADL to find the correct overloads for log10 et.al. 88 using namespace std; 89 90 bool precexp_offset = false; 91 U dim = 0; 92 if (0 == (Policies::fmtflags::fixed & flags) && !traits::test_zero(n)) 93 { 94 dim = log10(n); 95 if (dim > 0) 96 n /= spirit::traits::pow10<U>(traits::truncate_to_long::call(dim)); 97 else if (n < 1.) { 98 long exp = traits::truncate_to_long::call(-dim); 99 100 dim = static_cast<U>(-exp); 101 102 // detect and handle denormalized numbers to prevent overflow in pow10 103 if (exp > std::numeric_limits<U>::max_exponent10) 104 { 105 n *= spirit::traits::pow10<U>(std::numeric_limits<U>::max_exponent10); 106 n *= spirit::traits::pow10<U>(exp - std::numeric_limits<U>::max_exponent10); 107 } 108 else 109 n *= spirit::traits::pow10<U>(exp); 110 111 if (n < 1.) 112 { 113 n *= 10.; 114 --dim; 115 precexp_offset = true; 116 } 117 } 118 } 119 120 // prepare numbers (sign, integer and fraction part) 121 U integer_part; 122 U precexp = spirit::traits::pow10<U>(precision); 123 U fractional_part = modf(n, &integer_part); 124 125 if (precexp_offset) 126 { 127 fractional_part = 128 floor((fractional_part * precexp + U(0.5)) * U(10.)) / U(10.); 129 } 130 else 131 { 132 fractional_part = floor(fractional_part * precexp + U(0.5)); 133 } 134 135 if (fractional_part >= precexp) 136 { 137 fractional_part = floor(fractional_part - precexp); 138 integer_part += 1; // handle rounding overflow 139 if (integer_part >= 10.) 140 { 141 integer_part /= 10.; 142 ++dim; 143 } 144 } 145 146 // if trailing zeros are to be omitted, normalize the precision and`` 147 // fractional part 148 U long_int_part = floor(integer_part); 149 U long_frac_part = fractional_part; 150 unsigned prec = precision; 151 if (!p.trailing_zeros(n)) 152 { 153 U frac_part_floor = long_frac_part; 154 if (0 != long_frac_part) { 155 // remove the trailing zeros 156 while (0 != prec && 157 0 == traits::remainder<10>::call(long_frac_part)) 158 { 159 long_frac_part = traits::divide<10>::call(long_frac_part); 160 --prec; 161 } 162 } 163 else { 164 // if the fractional part is zero, we don't need to output 165 // any additional digits 166 prec = 0; 167 } 168 169 if (precision != prec) 170 { 171 long_frac_part = frac_part_floor / 172 spirit::traits::pow10<U>(precision-prec); 173 } 174 } 175 176 // call the actual generating functions to output the different parts 177 if ((force_sign || sign_val) && 178 traits::test_zero(long_int_part) && 179 traits::test_zero(long_frac_part)) 180 { 181 sign_val = false; // result is zero, no sign please 182 force_sign = false; 183 } 184 185 // generate integer part 186 bool r = p.integer_part(sink, long_int_part, sign_val, force_sign); 187 188 // generate decimal point 189 r = r && p.dot(sink, long_frac_part, precision); 190 191 // generate fractional part with the desired precision 192 r = r && p.fraction_part(sink, long_frac_part, prec, precision); 193 194 if (r && 0 == (Policies::fmtflags::fixed & flags)) { 195 return p.template exponent<CharEncoding, Tag>(sink, 196 traits::truncate_to_long::call(dim)); 197 } 198 return r; 199 } 200 201 #if BOOST_WORKAROUND(BOOST_MSVC, >= 1400) 202 # pragma warning(pop) 203 #endif 204 205 }; 206 }}} 207 208 #endif 209 210