annotate DEPENDENCIES/generic/include/boost/multiprecision/detail/float_string_cvt.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 ///////////////////////////////////////////////////////////////
Chris@16 2 // Copyright 2013 John Maddock. Distributed under the Boost
Chris@16 3 // Software License, Version 1.0. (See accompanying file
Chris@16 4 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_
Chris@16 5 //
Chris@16 6 // Generic routines for converting floating point values to and from decimal strings.
Chris@16 7 // Note that these use "naive" algorithms which result in rounding error - so they
Chris@16 8 // do not round trip to and from the string representation (but should only be out
Chris@16 9 // in the last bit).
Chris@16 10 //
Chris@16 11
Chris@16 12 #ifndef BOOST_MP_FLOAT_STRING_CVT_HPP
Chris@16 13 #define BOOST_MP_FLOAT_STRING_CVT_HPP
Chris@16 14
Chris@16 15 #include <cctype>
Chris@16 16
Chris@16 17 namespace boost{ namespace multiprecision{ namespace detail{
Chris@16 18
Chris@101 19 template <class I>
Chris@101 20 inline void round_string_up_at(std::string& s, int pos, I& expon)
Chris@16 21 {
Chris@16 22 //
Chris@16 23 // Rounds up a string representation of a number at pos:
Chris@16 24 //
Chris@16 25 if(pos < 0)
Chris@16 26 {
Chris@101 27 s.insert(static_cast<std::string::size_type>(0), 1, '1');
Chris@16 28 s.erase(s.size() - 1);
Chris@16 29 ++expon;
Chris@16 30 }
Chris@16 31 else if(s[pos] == '9')
Chris@16 32 {
Chris@16 33 s[pos] = '0';
Chris@16 34 round_string_up_at(s, pos - 1, expon);
Chris@16 35 }
Chris@16 36 else
Chris@16 37 {
Chris@16 38 if((pos == 0) && (s[pos] == '0') && (s.size() == 1))
Chris@16 39 ++expon;
Chris@16 40 ++s[pos];
Chris@16 41 }
Chris@16 42 }
Chris@16 43
Chris@16 44 template <class Backend>
Chris@16 45 std::string convert_to_string(Backend b, std::streamsize digits, std::ios_base::fmtflags f)
Chris@16 46 {
Chris@16 47 using default_ops::eval_log10;
Chris@16 48 using default_ops::eval_floor;
Chris@16 49 using default_ops::eval_pow;
Chris@16 50 using default_ops::eval_convert_to;
Chris@16 51 using default_ops::eval_multiply;
Chris@16 52 using default_ops::eval_divide;
Chris@16 53 using default_ops::eval_subtract;
Chris@16 54 using default_ops::eval_fpclassify;
Chris@16 55
Chris@16 56 typedef typename mpl::front<typename Backend::unsigned_types>::type ui_type;
Chris@16 57 typedef typename Backend::exponent_type exponent_type;
Chris@16 58
Chris@16 59 std::string result;
Chris@16 60 bool iszero = false;
Chris@16 61 bool isneg = false;
Chris@16 62 exponent_type expon = 0;
Chris@16 63 std::streamsize org_digits = digits;
Chris@16 64 BOOST_ASSERT(digits > 0);
Chris@16 65
Chris@16 66 int fpt = eval_fpclassify(b);
Chris@16 67
Chris@101 68 if(fpt == (int)FP_ZERO)
Chris@16 69 {
Chris@16 70 result = "0";
Chris@16 71 iszero = true;
Chris@16 72 }
Chris@101 73 else if(fpt == (int)FP_INFINITE)
Chris@16 74 {
Chris@16 75 if(b.compare(ui_type(0)) < 0)
Chris@16 76 return "-inf";
Chris@16 77 else
Chris@16 78 return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf";
Chris@16 79 }
Chris@101 80 else if(fpt == (int)FP_NAN)
Chris@16 81 {
Chris@16 82 return "nan";
Chris@16 83 }
Chris@16 84 else
Chris@16 85 {
Chris@16 86 //
Chris@16 87 // Start by figuring out the exponent:
Chris@16 88 //
Chris@16 89 isneg = b.compare(ui_type(0)) < 0;
Chris@16 90 if(isneg)
Chris@16 91 b.negate();
Chris@16 92 Backend t;
Chris@16 93 Backend ten;
Chris@16 94 ten = ui_type(10);
Chris@16 95
Chris@16 96 eval_log10(t, b);
Chris@16 97 eval_floor(t, t);
Chris@16 98 eval_convert_to(&expon, t);
Chris@16 99 if(-expon > std::numeric_limits<number<Backend> >::max_exponent10 - 3)
Chris@16 100 {
Chris@16 101 int e = -expon / 2;
Chris@16 102 Backend t2;
Chris@16 103 eval_pow(t2, ten, e);
Chris@16 104 eval_multiply(t, t2, b);
Chris@16 105 eval_multiply(t, t2);
Chris@16 106 if(expon & 1)
Chris@16 107 eval_multiply(t, ten);
Chris@16 108 }
Chris@16 109 else
Chris@16 110 {
Chris@16 111 eval_pow(t, ten, -expon);
Chris@16 112 eval_multiply(t, b);
Chris@16 113 }
Chris@16 114 //
Chris@16 115 // Make sure we're between [1,10) and adjust if not:
Chris@16 116 //
Chris@16 117 if(t.compare(ui_type(1)) < 0)
Chris@16 118 {
Chris@16 119 eval_multiply(t, ui_type(10));
Chris@16 120 --expon;
Chris@16 121 }
Chris@16 122 else if(t.compare(ui_type(10)) >= 0)
Chris@16 123 {
Chris@16 124 eval_divide(t, ui_type(10));
Chris@16 125 ++expon;
Chris@16 126 }
Chris@16 127 Backend digit;
Chris@16 128 ui_type cdigit;
Chris@16 129 //
Chris@16 130 // Adjust the number of digits required based on formatting options:
Chris@16 131 //
Chris@16 132 if(((f & std::ios_base::fixed) == std::ios_base::fixed) && (expon != -1))
Chris@16 133 digits += expon + 1;
Chris@16 134 if((f & std::ios_base::scientific) == std::ios_base::scientific)
Chris@16 135 ++digits;
Chris@16 136 //
Chris@16 137 // Extract the digits one at a time:
Chris@16 138 //
Chris@16 139 for(unsigned i = 0; i < digits; ++i)
Chris@16 140 {
Chris@16 141 eval_floor(digit, t);
Chris@16 142 eval_convert_to(&cdigit, digit);
Chris@16 143 result += static_cast<char>('0' + cdigit);
Chris@16 144 eval_subtract(t, digit);
Chris@16 145 eval_multiply(t, ten);
Chris@16 146 }
Chris@16 147 //
Chris@16 148 // Possibly round result:
Chris@16 149 //
Chris@16 150 if(digits >= 0)
Chris@16 151 {
Chris@16 152 eval_floor(digit, t);
Chris@16 153 eval_convert_to(&cdigit, digit);
Chris@16 154 eval_subtract(t, digit);
Chris@16 155 if((cdigit == 5) && (t.compare(ui_type(0)) == 0))
Chris@16 156 {
Chris@16 157 // Bankers rounding:
Chris@16 158 if((*result.rbegin() - '0') & 1)
Chris@16 159 {
Chris@16 160 round_string_up_at(result, result.size() - 1, expon);
Chris@16 161 }
Chris@16 162 }
Chris@16 163 else if(cdigit >= 5)
Chris@16 164 {
Chris@16 165 round_string_up_at(result, result.size() - 1, expon);
Chris@16 166 }
Chris@16 167 }
Chris@16 168 }
Chris@16 169 while((result.size() > digits) && result.size())
Chris@16 170 {
Chris@16 171 // We may get here as a result of rounding...
Chris@16 172 if(result.size() > 1)
Chris@16 173 result.erase(result.size() - 1);
Chris@16 174 else
Chris@16 175 {
Chris@16 176 if(expon > 0)
Chris@16 177 --expon; // so we put less padding in the result.
Chris@16 178 else
Chris@16 179 ++expon;
Chris@16 180 ++digits;
Chris@16 181 }
Chris@16 182 }
Chris@16 183 BOOST_ASSERT(org_digits >= 0);
Chris@16 184 if(isneg)
Chris@101 185 result.insert(static_cast<std::string::size_type>(0), 1, '-');
Chris@16 186 format_float_string(result, expon, org_digits, f, iszero);
Chris@16 187
Chris@16 188 return result;
Chris@16 189 }
Chris@16 190
Chris@16 191 template <class Backend>
Chris@16 192 void convert_from_string(Backend& b, const char* p)
Chris@16 193 {
Chris@16 194 using default_ops::eval_multiply;
Chris@16 195 using default_ops::eval_add;
Chris@16 196 using default_ops::eval_pow;
Chris@16 197 using default_ops::eval_divide;
Chris@16 198
Chris@16 199 typedef typename mpl::front<typename Backend::unsigned_types>::type ui_type;
Chris@16 200 b = ui_type(0);
Chris@16 201 if(!p || (*p == 0))
Chris@16 202 return;
Chris@16 203
Chris@16 204 bool is_neg = false;
Chris@16 205 bool is_neg_expon = false;
Chris@16 206 static const ui_type ten = ui_type(10);
Chris@16 207 typename Backend::exponent_type expon = 0;
Chris@16 208 int digits_seen = 0;
Chris@16 209 typedef std::numeric_limits<number<Backend, et_off> > limits;
Chris@16 210 static const int max_digits = limits::is_specialized ? limits::max_digits10 + 1 : INT_MAX;
Chris@16 211
Chris@16 212 if(*p == '+') ++p;
Chris@16 213 else if(*p == '-')
Chris@16 214 {
Chris@16 215 is_neg = true;
Chris@16 216 ++p;
Chris@16 217 }
Chris@16 218 if((std::strcmp(p, "nan") == 0) || (std::strcmp(p, "NaN") == 0) || (std::strcmp(p, "NAN") == 0))
Chris@16 219 {
Chris@16 220 eval_divide(b, ui_type(0));
Chris@16 221 if(is_neg)
Chris@16 222 b.negate();
Chris@16 223 return;
Chris@16 224 }
Chris@16 225 if((std::strcmp(p, "inf") == 0) || (std::strcmp(p, "Inf") == 0) || (std::strcmp(p, "INF") == 0))
Chris@16 226 {
Chris@16 227 b = ui_type(1);
Chris@16 228 eval_divide(b, ui_type(0));
Chris@16 229 if(is_neg)
Chris@16 230 b.negate();
Chris@16 231 return;
Chris@16 232 }
Chris@16 233 //
Chris@16 234 // Grab all the leading digits before the decimal point:
Chris@16 235 //
Chris@16 236 while(std::isdigit(*p))
Chris@16 237 {
Chris@16 238 eval_multiply(b, ten);
Chris@16 239 eval_add(b, ui_type(*p - '0'));
Chris@16 240 ++p;
Chris@16 241 ++digits_seen;
Chris@16 242 }
Chris@16 243 if(*p == '.')
Chris@16 244 {
Chris@16 245 //
Chris@16 246 // Grab everything after the point, stop when we've seen
Chris@16 247 // enough digits, even if there are actually more available:
Chris@16 248 //
Chris@16 249 ++p;
Chris@16 250 while(std::isdigit(*p))
Chris@16 251 {
Chris@16 252 eval_multiply(b, ten);
Chris@16 253 eval_add(b, ui_type(*p - '0'));
Chris@16 254 ++p;
Chris@16 255 --expon;
Chris@16 256 if(++digits_seen > max_digits)
Chris@16 257 break;
Chris@16 258 }
Chris@16 259 while(std::isdigit(*p))
Chris@16 260 ++p;
Chris@16 261 }
Chris@16 262 //
Chris@16 263 // Parse the exponent:
Chris@16 264 //
Chris@16 265 if((*p == 'e') || (*p == 'E'))
Chris@16 266 {
Chris@16 267 ++p;
Chris@16 268 if(*p == '+') ++p;
Chris@16 269 else if(*p == '-')
Chris@16 270 {
Chris@16 271 is_neg_expon = true;
Chris@16 272 ++p;
Chris@16 273 }
Chris@16 274 typename Backend::exponent_type e2 = 0;
Chris@16 275 while(std::isdigit(*p))
Chris@16 276 {
Chris@16 277 e2 *= 10;
Chris@16 278 e2 += (*p - '0');
Chris@16 279 ++p;
Chris@16 280 }
Chris@16 281 if(is_neg_expon)
Chris@16 282 e2 = -e2;
Chris@16 283 expon += e2;
Chris@16 284 }
Chris@16 285 if(expon)
Chris@16 286 {
Chris@16 287 // Scale by 10^expon, note that 10^expon can be
Chris@16 288 // outside the range of our number type, even though the
Chris@16 289 // result is within range, if that looks likely, then split
Chris@16 290 // the calculation in two:
Chris@16 291 Backend t;
Chris@16 292 t = ten;
Chris@16 293 if(expon > limits::min_exponent10 + 2)
Chris@16 294 {
Chris@16 295 eval_pow(t, t, expon);
Chris@16 296 eval_multiply(b, t);
Chris@16 297 }
Chris@16 298 else
Chris@16 299 {
Chris@16 300 eval_pow(t, t, expon + digits_seen + 1);
Chris@16 301 eval_multiply(b, t);
Chris@16 302 t = ten;
Chris@16 303 eval_pow(t, t, -digits_seen - 1);
Chris@16 304 eval_multiply(b, t);
Chris@16 305 }
Chris@16 306 }
Chris@16 307 if(is_neg)
Chris@16 308 b.negate();
Chris@16 309 if(*p)
Chris@16 310 {
Chris@16 311 // Unexpected input in string:
Chris@16 312 BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected characters in string being interpreted as a float128."));
Chris@16 313 }
Chris@16 314 }
Chris@16 315
Chris@16 316 }}} // namespaces
Chris@16 317
Chris@16 318 #endif