Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:42:21

0001 ///////////////////////////////////////////////////////////////
0002 //  Copyright 2013 John Maddock. Distributed under the Boost
0003 //  Software License, Version 1.0. (See accompanying file
0004 //  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
0005 //
0006 // Generic routines for converting floating point values to and from decimal strings.
0007 // Note that these use "naive" algorithms which result in rounding error - so they
0008 // do not round trip to and from the string representation (but should only be out
0009 // in the last bit).
0010 //
0011 
0012 #ifndef BOOST_MP_FLOAT_STRING_CVT_HPP
0013 #define BOOST_MP_FLOAT_STRING_CVT_HPP
0014 
0015 #include <string>
0016 #include <cctype>
0017 #include <boost/multiprecision/detail/no_exceptions_support.hpp>
0018 #include <boost/multiprecision/detail/assert.hpp>
0019 
0020 namespace boost { namespace multiprecision { namespace detail {
0021 
0022 template <class I>
0023 inline void round_string_up_at(std::string& s, std::ptrdiff_t pos, I& expon)
0024 {
0025    //
0026    // Rounds up a string representation of a number at pos:
0027    //
0028    if (pos < 0)
0029    {
0030       s.insert(static_cast<std::string::size_type>(0), 1, '1');
0031       s.erase(s.size() - 1);
0032       ++expon;
0033    }
0034    else if (s[static_cast<std::size_t>(pos)] == '9')
0035    {
0036       s[static_cast<std::size_t>(pos)] = '0';
0037       round_string_up_at(s, pos - 1, expon);
0038    }
0039    else
0040    {
0041       if ((pos == 0) && (s[static_cast<std::size_t>(pos)] == '0') && (s.size() == 1))
0042          ++expon;
0043       ++s[static_cast<std::size_t>(pos)];
0044    }
0045 }
0046 
0047 template <class Backend>
0048 std::string convert_to_string(Backend b, std::streamsize digits, std::ios_base::fmtflags f)
0049 {
0050    using default_ops::eval_convert_to;
0051    using default_ops::eval_divide;
0052    using default_ops::eval_floor;
0053    using default_ops::eval_fpclassify;
0054    using default_ops::eval_log10;
0055    using default_ops::eval_multiply;
0056    using default_ops::eval_pow;
0057    using default_ops::eval_subtract;
0058 
0059    using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
0060    using exponent_type = typename Backend::exponent_type                            ;
0061 
0062    std::string     result;
0063    bool            iszero     = false;
0064    bool            isneg      = false;
0065    exponent_type   expon      = 0;
0066    std::streamsize org_digits = digits;
0067    BOOST_MP_ASSERT(digits > 0);
0068 
0069    int fpt = eval_fpclassify(b);
0070 
0071    if (fpt == static_cast<int>(FP_ZERO))
0072    {
0073       result = "0";
0074       iszero = true;
0075    }
0076    else if (fpt == static_cast<int>(FP_INFINITE))
0077    {
0078       if (b.compare(ui_type(0)) < 0)
0079          return "-inf";
0080       else
0081          return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf";
0082    }
0083    else if (fpt == static_cast<int>(FP_NAN))
0084    {
0085       return "nan";
0086    }
0087    else
0088    {
0089       //
0090       // Start by figuring out the exponent:
0091       //
0092       isneg = b.compare(ui_type(0)) < 0;
0093       if (isneg)
0094          b.negate();
0095       Backend t;
0096       Backend ten;
0097       ten = ui_type(10);
0098 
0099       eval_log10(t, b);
0100       eval_floor(t, t);
0101       eval_convert_to(&expon, t);
0102       if (-expon > std::numeric_limits<number<Backend> >::max_exponent10 - 3)
0103       {
0104          int     e = -expon / 2;
0105          Backend t2;
0106          eval_pow(t2, ten, e);
0107          eval_multiply(t, t2, b);
0108          eval_multiply(t, t2);
0109          if (expon & 1)
0110             eval_multiply(t, ten);
0111       }
0112       else
0113       {
0114          eval_pow(t, ten, -expon);
0115          eval_multiply(t, b);
0116       }
0117       //
0118       // Make sure we're between [1,10) and adjust if not:
0119       //
0120       if (t.compare(ui_type(1)) < 0)
0121       {
0122          eval_multiply(t, ui_type(10));
0123          --expon;
0124       }
0125       else if (t.compare(ui_type(10)) >= 0)
0126       {
0127          eval_divide(t, ui_type(10));
0128          ++expon;
0129       }
0130       Backend digit;
0131       ui_type cdigit;
0132       //
0133       // Adjust the number of digits required based on formatting options:
0134       //
0135       if (((f & std::ios_base::fixed) == std::ios_base::fixed) && (expon != -1))
0136          digits += expon + 1;
0137       if ((f & std::ios_base::scientific) == std::ios_base::scientific)
0138          ++digits;
0139       //
0140       // Extract the digits one at a time:
0141       //
0142       for (unsigned i = 0; i < digits; ++i)
0143       {
0144          eval_floor(digit, t);
0145          eval_convert_to(&cdigit, digit);
0146          result += static_cast<char>('0' + cdigit);
0147          eval_subtract(t, digit);
0148          eval_multiply(t, ten);
0149       }
0150       //
0151       // Possibly round result:
0152       //
0153       if (digits >= 0)
0154       {
0155          eval_floor(digit, t);
0156          eval_convert_to(&cdigit, digit);
0157          eval_subtract(t, digit);
0158          if ((cdigit == 5) && (t.compare(ui_type(0)) == 0))
0159          {
0160             // Bankers rounding:
0161             if ((*result.rbegin() - '0') & 1)
0162             {
0163                round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
0164             }
0165          }
0166          else if (cdigit >= 5)
0167          {
0168             round_string_up_at(result, static_cast<std::ptrdiff_t>(result.size() - 1u), expon);
0169          }
0170       }
0171       eval_floor(t, b);
0172       if ((t.compare(b) == 0) && (static_cast<std::size_t>(expon + 1) < result.size()))
0173       {
0174          // Input is an integer, sometimes we get a result which is not an integer here as a result of printing too
0175          // many digits, so lets round if required:
0176          round_string_up_at(result, expon + 1, expon);
0177          result.erase(static_cast<std::string::size_type>(expon + 1));
0178       }
0179    }
0180    while ((static_cast<std::streamsize>(result.size()) > digits) && (result.size() != 0U))
0181    {
0182       // We may get here as a result of rounding...
0183       if (result.size() > 1)
0184          result.erase(result.size() - 1);
0185       else
0186       {
0187          if (expon > 0)
0188             --expon; // so we put less padding in the result.
0189          else
0190             ++expon;
0191          ++digits;
0192       }
0193    }
0194    BOOST_MP_ASSERT(org_digits >= 0);
0195    if (isneg)
0196       result.insert(static_cast<std::string::size_type>(0), 1, '-');
0197    format_float_string(result, expon, org_digits, f, iszero);
0198 
0199    return result;
0200 }
0201 
0202 template <class Backend>
0203 void convert_from_string(Backend& b, const char* p)
0204 {
0205    using default_ops::eval_add;
0206    using default_ops::eval_divide;
0207    using default_ops::eval_multiply;
0208    using default_ops::eval_pow;
0209 
0210    using ui_type = typename std::tuple_element<0, typename Backend::unsigned_types>::type;
0211    b = ui_type(0);
0212    if (!p || (*p == 0))
0213       return;
0214 
0215    bool                            is_neg       = false;
0216    bool                            is_neg_expon = false;
0217    constexpr ui_type               ten          = ui_type(10);
0218    typename Backend::exponent_type expon        = 0;
0219    int                             digits_seen  = 0;
0220 
0221    using limits = std::numeric_limits<number<Backend, et_off>>;
0222 
0223    constexpr int max_digits = limits::is_specialized ? limits::max_digits10 + 1 : INT_MAX;
0224 
0225    if (*p == '+')
0226       ++p;
0227    else if (*p == '-')
0228    {
0229       is_neg = true;
0230       ++p;
0231    }
0232    if ((std::strcmp(p, "nan") == 0) || (std::strcmp(p, "NaN") == 0) || (std::strcmp(p, "NAN") == 0))
0233    {
0234       eval_divide(b, ui_type(0));
0235       if (is_neg)
0236          b.negate();
0237       return;
0238    }
0239    if ((std::strcmp(p, "inf") == 0) || (std::strcmp(p, "Inf") == 0) || (std::strcmp(p, "INF") == 0))
0240    {
0241       b = ui_type(1);
0242       eval_divide(b, ui_type(0));
0243       if (is_neg)
0244          b.negate();
0245       return;
0246    }
0247    //
0248    // Grab all the leading digits before the decimal point:
0249    //
0250    while (std::isdigit(*p))
0251    {
0252       eval_multiply(b, ten);
0253       eval_add(b, ui_type(*p - '0'));
0254       ++p;
0255       ++digits_seen;
0256    }
0257    if (*p == '.')
0258    {
0259       //
0260       // Grab everything after the point, stop when we've seen
0261       // enough digits, even if there are actually more available:
0262       //
0263       ++p;
0264       while (std::isdigit(*p))
0265       {
0266          eval_multiply(b, ten);
0267          eval_add(b, ui_type(*p - '0'));
0268          ++p;
0269          --expon;
0270          if (++digits_seen > max_digits)
0271             break;
0272       }
0273       while (std::isdigit(*p))
0274          ++p;
0275    }
0276    //
0277    // Parse the exponent:
0278    //
0279    if ((*p == 'e') || (*p == 'E'))
0280    {
0281       ++p;
0282       if (*p == '+')
0283          ++p;
0284       else if (*p == '-')
0285       {
0286          is_neg_expon = true;
0287          ++p;
0288       }
0289       typename Backend::exponent_type e2 = 0;
0290       while (std::isdigit(*p))
0291       {
0292          e2 *= 10;
0293          e2 += (*p - '0');
0294          ++p;
0295       }
0296       if (is_neg_expon)
0297          e2 = -e2;
0298       expon += e2;
0299    }
0300    if (expon)
0301    {
0302       // Scale by 10^expon, note that 10^expon can be
0303       // outside the range of our number type, even though the
0304       // result is within range, if that looks likely, then split
0305       // the calculation in two:
0306       Backend t;
0307       t = ten;
0308       if (expon > limits::min_exponent10 + 2)
0309       {
0310          eval_pow(t, t, expon);
0311          eval_multiply(b, t);
0312       }
0313       else
0314       {
0315          eval_pow(t, t, expon + digits_seen + 1);
0316          eval_multiply(b, t);
0317          t = ten;
0318          eval_pow(t, t, -digits_seen - 1);
0319          eval_multiply(b, t);
0320       }
0321    }
0322    if (is_neg)
0323       b.negate();
0324    if (*p)
0325    {
0326       // Unexpected input in string:
0327       BOOST_MP_THROW_EXCEPTION(std::runtime_error("Unexpected characters in string being interpreted as a float128."));
0328    }
0329 }
0330 
0331 }}} // namespace boost::multiprecision::detail
0332 
0333 #endif