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