Chris@16: /////////////////////////////////////////////////////////////// Chris@16: // Copyright 2013 John Maddock. Distributed under the Boost Chris@16: // Software License, Version 1.0. (See accompanying file Chris@16: // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_ Chris@16: // Chris@16: // Generic routines for converting floating point values to and from decimal strings. Chris@16: // Note that these use "naive" algorithms which result in rounding error - so they Chris@16: // do not round trip to and from the string representation (but should only be out Chris@16: // in the last bit). Chris@16: // Chris@16: Chris@16: #ifndef BOOST_MP_FLOAT_STRING_CVT_HPP Chris@16: #define BOOST_MP_FLOAT_STRING_CVT_HPP Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost{ namespace multiprecision{ namespace detail{ Chris@16: Chris@101: template Chris@101: inline void round_string_up_at(std::string& s, int pos, I& expon) Chris@16: { Chris@16: // Chris@16: // Rounds up a string representation of a number at pos: Chris@16: // Chris@16: if(pos < 0) Chris@16: { Chris@101: s.insert(static_cast(0), 1, '1'); Chris@16: s.erase(s.size() - 1); Chris@16: ++expon; Chris@16: } Chris@16: else if(s[pos] == '9') Chris@16: { Chris@16: s[pos] = '0'; Chris@16: round_string_up_at(s, pos - 1, expon); Chris@16: } Chris@16: else Chris@16: { Chris@16: if((pos == 0) && (s[pos] == '0') && (s.size() == 1)) Chris@16: ++expon; Chris@16: ++s[pos]; Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: std::string convert_to_string(Backend b, std::streamsize digits, std::ios_base::fmtflags f) Chris@16: { Chris@16: using default_ops::eval_log10; Chris@16: using default_ops::eval_floor; Chris@16: using default_ops::eval_pow; Chris@16: using default_ops::eval_convert_to; Chris@16: using default_ops::eval_multiply; Chris@16: using default_ops::eval_divide; Chris@16: using default_ops::eval_subtract; Chris@16: using default_ops::eval_fpclassify; Chris@16: Chris@16: typedef typename mpl::front::type ui_type; Chris@16: typedef typename Backend::exponent_type exponent_type; Chris@16: Chris@16: std::string result; Chris@16: bool iszero = false; Chris@16: bool isneg = false; Chris@16: exponent_type expon = 0; Chris@16: std::streamsize org_digits = digits; Chris@16: BOOST_ASSERT(digits > 0); Chris@16: Chris@16: int fpt = eval_fpclassify(b); Chris@16: Chris@101: if(fpt == (int)FP_ZERO) Chris@16: { Chris@16: result = "0"; Chris@16: iszero = true; Chris@16: } Chris@101: else if(fpt == (int)FP_INFINITE) Chris@16: { Chris@16: if(b.compare(ui_type(0)) < 0) Chris@16: return "-inf"; Chris@16: else Chris@16: return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf"; Chris@16: } Chris@101: else if(fpt == (int)FP_NAN) Chris@16: { Chris@16: return "nan"; Chris@16: } Chris@16: else Chris@16: { Chris@16: // Chris@16: // Start by figuring out the exponent: Chris@16: // Chris@16: isneg = b.compare(ui_type(0)) < 0; Chris@16: if(isneg) Chris@16: b.negate(); Chris@16: Backend t; Chris@16: Backend ten; Chris@16: ten = ui_type(10); Chris@16: Chris@16: eval_log10(t, b); Chris@16: eval_floor(t, t); Chris@16: eval_convert_to(&expon, t); Chris@16: if(-expon > std::numeric_limits >::max_exponent10 - 3) Chris@16: { Chris@16: int e = -expon / 2; Chris@16: Backend t2; Chris@16: eval_pow(t2, ten, e); Chris@16: eval_multiply(t, t2, b); Chris@16: eval_multiply(t, t2); Chris@16: if(expon & 1) Chris@16: eval_multiply(t, ten); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_pow(t, ten, -expon); Chris@16: eval_multiply(t, b); Chris@16: } Chris@16: // Chris@16: // Make sure we're between [1,10) and adjust if not: Chris@16: // Chris@16: if(t.compare(ui_type(1)) < 0) Chris@16: { Chris@16: eval_multiply(t, ui_type(10)); Chris@16: --expon; Chris@16: } Chris@16: else if(t.compare(ui_type(10)) >= 0) Chris@16: { Chris@16: eval_divide(t, ui_type(10)); Chris@16: ++expon; Chris@16: } Chris@16: Backend digit; Chris@16: ui_type cdigit; Chris@16: // Chris@16: // Adjust the number of digits required based on formatting options: Chris@16: // Chris@16: if(((f & std::ios_base::fixed) == std::ios_base::fixed) && (expon != -1)) Chris@16: digits += expon + 1; Chris@16: if((f & std::ios_base::scientific) == std::ios_base::scientific) Chris@16: ++digits; Chris@16: // Chris@16: // Extract the digits one at a time: Chris@16: // Chris@16: for(unsigned i = 0; i < digits; ++i) Chris@16: { Chris@16: eval_floor(digit, t); Chris@16: eval_convert_to(&cdigit, digit); Chris@16: result += static_cast('0' + cdigit); Chris@16: eval_subtract(t, digit); Chris@16: eval_multiply(t, ten); Chris@16: } Chris@16: // Chris@16: // Possibly round result: Chris@16: // Chris@16: if(digits >= 0) Chris@16: { Chris@16: eval_floor(digit, t); Chris@16: eval_convert_to(&cdigit, digit); Chris@16: eval_subtract(t, digit); Chris@16: if((cdigit == 5) && (t.compare(ui_type(0)) == 0)) Chris@16: { Chris@16: // Bankers rounding: Chris@16: if((*result.rbegin() - '0') & 1) Chris@16: { Chris@16: round_string_up_at(result, result.size() - 1, expon); Chris@16: } Chris@16: } Chris@16: else if(cdigit >= 5) Chris@16: { Chris@16: round_string_up_at(result, result.size() - 1, expon); Chris@16: } Chris@16: } Chris@16: } Chris@16: while((result.size() > digits) && result.size()) Chris@16: { Chris@16: // We may get here as a result of rounding... Chris@16: if(result.size() > 1) Chris@16: result.erase(result.size() - 1); Chris@16: else Chris@16: { Chris@16: if(expon > 0) Chris@16: --expon; // so we put less padding in the result. Chris@16: else Chris@16: ++expon; Chris@16: ++digits; Chris@16: } Chris@16: } Chris@16: BOOST_ASSERT(org_digits >= 0); Chris@16: if(isneg) Chris@101: result.insert(static_cast(0), 1, '-'); Chris@16: format_float_string(result, expon, org_digits, f, iszero); Chris@16: Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: void convert_from_string(Backend& b, const char* p) Chris@16: { Chris@16: using default_ops::eval_multiply; Chris@16: using default_ops::eval_add; Chris@16: using default_ops::eval_pow; Chris@16: using default_ops::eval_divide; Chris@16: Chris@16: typedef typename mpl::front::type ui_type; Chris@16: b = ui_type(0); Chris@16: if(!p || (*p == 0)) Chris@16: return; Chris@16: Chris@16: bool is_neg = false; Chris@16: bool is_neg_expon = false; Chris@16: static const ui_type ten = ui_type(10); Chris@16: typename Backend::exponent_type expon = 0; Chris@16: int digits_seen = 0; Chris@16: typedef std::numeric_limits > limits; Chris@16: static const int max_digits = limits::is_specialized ? limits::max_digits10 + 1 : INT_MAX; Chris@16: Chris@16: if(*p == '+') ++p; Chris@16: else if(*p == '-') Chris@16: { Chris@16: is_neg = true; Chris@16: ++p; Chris@16: } Chris@16: if((std::strcmp(p, "nan") == 0) || (std::strcmp(p, "NaN") == 0) || (std::strcmp(p, "NAN") == 0)) Chris@16: { Chris@16: eval_divide(b, ui_type(0)); Chris@16: if(is_neg) Chris@16: b.negate(); Chris@16: return; Chris@16: } Chris@16: if((std::strcmp(p, "inf") == 0) || (std::strcmp(p, "Inf") == 0) || (std::strcmp(p, "INF") == 0)) Chris@16: { Chris@16: b = ui_type(1); Chris@16: eval_divide(b, ui_type(0)); Chris@16: if(is_neg) Chris@16: b.negate(); Chris@16: return; Chris@16: } Chris@16: // Chris@16: // Grab all the leading digits before the decimal point: Chris@16: // Chris@16: while(std::isdigit(*p)) Chris@16: { Chris@16: eval_multiply(b, ten); Chris@16: eval_add(b, ui_type(*p - '0')); Chris@16: ++p; Chris@16: ++digits_seen; Chris@16: } Chris@16: if(*p == '.') Chris@16: { Chris@16: // Chris@16: // Grab everything after the point, stop when we've seen Chris@16: // enough digits, even if there are actually more available: Chris@16: // Chris@16: ++p; Chris@16: while(std::isdigit(*p)) Chris@16: { Chris@16: eval_multiply(b, ten); Chris@16: eval_add(b, ui_type(*p - '0')); Chris@16: ++p; Chris@16: --expon; Chris@16: if(++digits_seen > max_digits) Chris@16: break; Chris@16: } Chris@16: while(std::isdigit(*p)) Chris@16: ++p; Chris@16: } Chris@16: // Chris@16: // Parse the exponent: Chris@16: // Chris@16: if((*p == 'e') || (*p == 'E')) Chris@16: { Chris@16: ++p; Chris@16: if(*p == '+') ++p; Chris@16: else if(*p == '-') Chris@16: { Chris@16: is_neg_expon = true; Chris@16: ++p; Chris@16: } Chris@16: typename Backend::exponent_type e2 = 0; Chris@16: while(std::isdigit(*p)) Chris@16: { Chris@16: e2 *= 10; Chris@16: e2 += (*p - '0'); Chris@16: ++p; Chris@16: } Chris@16: if(is_neg_expon) Chris@16: e2 = -e2; Chris@16: expon += e2; Chris@16: } Chris@16: if(expon) Chris@16: { Chris@16: // Scale by 10^expon, note that 10^expon can be Chris@16: // outside the range of our number type, even though the Chris@16: // result is within range, if that looks likely, then split Chris@16: // the calculation in two: Chris@16: Backend t; Chris@16: t = ten; Chris@16: if(expon > limits::min_exponent10 + 2) Chris@16: { Chris@16: eval_pow(t, t, expon); Chris@16: eval_multiply(b, t); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_pow(t, t, expon + digits_seen + 1); Chris@16: eval_multiply(b, t); Chris@16: t = ten; Chris@16: eval_pow(t, t, -digits_seen - 1); Chris@16: eval_multiply(b, t); Chris@16: } Chris@16: } Chris@16: if(is_neg) Chris@16: b.negate(); Chris@16: if(*p) Chris@16: { Chris@16: // Unexpected input in string: Chris@16: BOOST_THROW_EXCEPTION(std::runtime_error("Unexpected characters in string being interpreted as a float128.")); Chris@16: } Chris@16: } Chris@16: Chris@16: }}} // namespaces Chris@16: Chris@16: #endif