123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822 |
- ///////////////////////////////////////////////////////////////////////////////
- // Copyright Christopher Kormanyos 2014.
- // Copyright John Maddock 2014.
- // Copyright Paul Bristow 2014.
- // 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_0.txt)
- //
- // Implement quadruple-precision I/O stream operations.
- #ifndef BOOST_MATH_CSTDFLOAT_IOSTREAM_2014_02_15_HPP_
- #define BOOST_MATH_CSTDFLOAT_IOSTREAM_2014_02_15_HPP_
- #include <boost/math/cstdfloat/cstdfloat_types.hpp>
- #include <boost/math/cstdfloat/cstdfloat_limits.hpp>
- #include <boost/math/cstdfloat/cstdfloat_cmath.hpp>
- #if defined(BOOST_CSTDFLOAT_NO_LIBQUADMATH_CMATH)
- #error You can not use <boost/math/cstdfloat/cstdfloat_iostream.hpp> with BOOST_CSTDFLOAT_NO_LIBQUADMATH_CMATH defined.
- #endif
- #if defined(BOOST_CSTDFLOAT_HAS_INTERNAL_FLOAT128_T) && defined(BOOST_MATH_USE_FLOAT128) && !defined(BOOST_CSTDFLOAT_NO_LIBQUADMATH_SUPPORT)
- #include <cstddef>
- #include <istream>
- #include <ostream>
- #include <sstream>
- #include <stdexcept>
- #include <string>
- #include <boost/math/tools/assert.hpp>
- #include <boost/math/tools/nothrow.hpp>
- #include <boost/math/tools/throw_exception.hpp>
- namespace boost {
- namespace math {
- namespace detail {
- //
- // What follows is the input streaming code: this is not "proper" iostream code at all
- // but that's hard to write.
- // For now just pull in all the characters that could possibly form the number
- // and let libquadmath's string parser make use of it. This fixes most use cases
- // including CSV type formats such as those used by the Random lib.
- //
- inline std::string read_string_while(std::istream& is, std::string const& permitted_chars)
- {
- std::ios_base::iostate state = std::ios_base::goodbit;
- const std::istream::sentry sentry_check(is);
- std::string result;
- if (sentry_check)
- {
- int c = is.rdbuf()->sgetc();
- for (;; c = is.rdbuf()->snextc())
- if (std::istream::traits_type::eq_int_type(std::istream::traits_type::eof(), c))
- { // end of file:
- state |= std::ios_base::eofbit;
- break;
- }
- else if (permitted_chars.find_first_of(std::istream::traits_type::to_char_type(c)) == std::string::npos)
- {
- // Invalid numeric character, stop reading:
- //is.rdbuf()->sputbackc(static_cast<char>(c));
- break;
- }
- else
- {
- result.append(1, std::istream::traits_type::to_char_type(c));
- }
- }
- if (!result.size())
- state |= std::ios_base::failbit;
- is.setstate(state);
- return result;
- }
- }
- }
- }
- #if defined(__GNUC__) && !defined(BOOST_MATH_TEST_IO_AS_INTEL_QUAD)
- // Forward declarations of quadruple-precision string functions.
- extern "C" int quadmath_snprintf(char *str, size_t size, const char *format, ...) BOOST_MATH_NOTHROW;
- extern "C" boost::math::cstdfloat::detail::float_internal128_t strtoflt128(const char*, char **) BOOST_MATH_NOTHROW;
- namespace std
- {
- template<typename char_type, class traits_type>
- inline std::basic_ostream<char_type, traits_type>& operator<<(std::basic_ostream<char_type, traits_type>& os, const boost::math::cstdfloat::detail::float_internal128_t& x)
- {
- std::basic_ostringstream<char_type, traits_type> ostr;
- ostr.flags(os.flags());
- ostr.imbue(os.getloc());
- ostr.precision(os.precision());
- char my_buffer[64U];
- const int my_prec = static_cast<int>(os.precision());
- const int my_digits = ((my_prec == 0) ? 36 : my_prec);
- const std::ios_base::fmtflags my_flags = os.flags();
- char my_format_string[8U];
- std::size_t my_format_string_index = 0U;
- my_format_string[my_format_string_index] = '%';
- ++my_format_string_index;
- if(my_flags & std::ios_base::showpos) { my_format_string[my_format_string_index] = '+'; ++my_format_string_index; }
- if(my_flags & std::ios_base::showpoint) { my_format_string[my_format_string_index] = '#'; ++my_format_string_index; }
- my_format_string[my_format_string_index + 0U] = '.';
- my_format_string[my_format_string_index + 1U] = '*';
- my_format_string[my_format_string_index + 2U] = 'Q';
- my_format_string_index += 3U;
- char the_notation_char;
- if (my_flags & std::ios_base::scientific) { the_notation_char = 'e'; }
- else if(my_flags & std::ios_base::fixed) { the_notation_char = 'f'; }
- else { the_notation_char = 'g'; }
- my_format_string[my_format_string_index + 0U] = the_notation_char;
- my_format_string[my_format_string_index + 1U] = 0;
- const int v = ::quadmath_snprintf(my_buffer,
- static_cast<int>(sizeof(my_buffer)),
- my_format_string,
- my_digits,
- x);
- if(v < 0) { BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Formatting of boost::float128_t failed internally in quadmath_snprintf().")); }
- if(v >= static_cast<int>(sizeof(my_buffer) - 1U))
- {
- // Evidently there is a really long floating-point string here,
- // such as a small decimal representation in non-scientific notation.
- // So we have to use dynamic memory allocation for the output
- // string buffer.
- char* my_buffer2 = nullptr;
- #ifndef BOOST_MATH_NO_EXCEPTIONS
- try
- {
- #endif
- my_buffer2 = new char[v + 3];
- #ifndef BOOST_MATH_NO_EXCEPTIONS
- }
- catch(const std::bad_alloc&)
- {
- BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Formatting of boost::float128_t failed while allocating memory."));
- }
- #endif
- const int v2 = ::quadmath_snprintf(my_buffer2,
- v + 3,
- my_format_string,
- my_digits,
- x);
- if(v2 >= v + 3)
- {
- BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Formatting of boost::float128_t failed."));
- }
- static_cast<void>(ostr << my_buffer2);
- delete [] my_buffer2;
- }
- else
- {
- static_cast<void>(ostr << my_buffer);
- }
- return (os << ostr.str());
- }
- template<typename char_type, class traits_type>
- inline std::basic_istream<char_type, traits_type>& operator>>(std::basic_istream<char_type, traits_type>& is, boost::math::cstdfloat::detail::float_internal128_t& x)
- {
- std::string str = boost::math::detail::read_string_while(is, "+-eE.0123456789infINFnanNANinfinityINFINITY");
- char* p_end;
- x = strtoflt128(str.c_str(), &p_end);
- if(static_cast<std::ptrdiff_t>(p_end - str.c_str()) != static_cast<std::ptrdiff_t>(str.length()))
- {
- for(std::string::const_reverse_iterator it = str.rbegin(); it != str.rend(); ++it)
- {
- static_cast<void>(is.putback(*it));
- }
- is.setstate(ios_base::failbit);
- BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Unable to interpret input string as a boost::float128_t"));
- }
- return is;
- }
- }
- #elif defined(__INTEL_COMPILER) || defined(BOOST_MATH_TEST_IO_AS_INTEL_QUAD)
- // The section for I/O stream support for the ICC compiler is particularly
- // long, because these functions must be painstakingly synthesized from
- // manually-written routines (ICC does not support I/O stream operations
- // for its _Quad type).
- // The following string-extraction routines are based on the methodology
- // used in Boost.Multiprecision by John Maddock and Christopher Kormanyos.
- // This methodology has been slightly modified here for boost::float128_t.
- #include <cstring>
- #include <cctype>
-
- namespace boost { namespace math { namespace cstdfloat { namespace detail {
- template<class string_type>
- void format_float_string(string_type& str,
- int my_exp,
- int digits,
- const std::ios_base::fmtflags f,
- const bool iszero)
- {
- typedef typename string_type::size_type size_type;
- const bool scientific = ((f & std::ios_base::scientific) == std::ios_base::scientific);
- const bool fixed = ((f & std::ios_base::fixed) == std::ios_base::fixed);
- const bool showpoint = ((f & std::ios_base::showpoint) == std::ios_base::showpoint);
- const bool showpos = ((f & std::ios_base::showpos) == std::ios_base::showpos);
- const bool b_neg = ((str.size() != 0U) && (str[0] == '-'));
- if(b_neg)
- {
- str.erase(0, 1);
- }
- if(digits == 0)
- {
- digits = static_cast<int>((std::max)(str.size(), size_type(16)));
- }
- if(iszero || str.empty() || (str.find_first_not_of('0') == string_type::npos))
- {
- // We will be printing zero, even though the value might not
- // actually be zero (it just may have been rounded to zero).
- str = "0";
- if(scientific || fixed)
- {
- str.append(1, '.');
- str.append(size_type(digits), '0');
- if(scientific)
- {
- str.append("e+00");
- }
- }
- else
- {
- if(showpoint)
- {
- str.append(1, '.');
- if(digits > 1)
- {
- str.append(size_type(digits - 1), '0');
- }
- }
- }
- if(b_neg)
- {
- str.insert(0U, 1U, '-');
- }
- else if(showpos)
- {
- str.insert(0U, 1U, '+');
- }
- return;
- }
- if(!fixed && !scientific && !showpoint)
- {
- // Suppress trailing zeros.
- typename string_type::iterator pos = str.end();
- while(pos != str.begin() && *--pos == '0') { ; }
- if(pos != str.end())
- {
- ++pos;
- }
- str.erase(pos, str.end());
- if(str.empty())
- {
- str = '0';
- }
- }
- else if(!fixed || (my_exp >= 0))
- {
- // Pad out the end with zero's if we need to.
- std::ptrdiff_t chars = static_cast<std::ptrdiff_t>(str.size());
- chars = digits - chars;
- if(scientific)
- {
- ++chars;
- }
- if(chars > 0)
- {
- str.append(static_cast<size_type>(chars), '0');
- }
- }
- if(fixed || (!scientific && (my_exp >= -4) && (my_exp < digits)))
- {
- if((1 + my_exp) > static_cast<int>(str.size()))
- {
- // Just pad out the end with zeros.
- str.append(static_cast<size_type>((1 + my_exp) - static_cast<int>(str.size())), '0');
- if(showpoint || fixed)
- {
- str.append(".");
- }
- }
- else if(my_exp + 1 < static_cast<int>(str.size()))
- {
- if(my_exp < 0)
- {
- str.insert(0U, static_cast<size_type>(-1 - my_exp), '0');
- str.insert(0U, "0.");
- }
- else
- {
- // Insert the decimal point:
- str.insert(static_cast<size_type>(my_exp + 1), 1, '.');
- }
- }
- else if(showpoint || fixed) // we have exactly the digits we require to left of the point
- {
- str += ".";
- }
- if(fixed)
- {
- // We may need to add trailing zeros.
- int l = static_cast<int>(str.find('.') + 1U);
- l = digits - (static_cast<int>(str.size()) - l);
- if(l > 0)
- {
- str.append(size_type(l), '0');
- }
- }
- }
- else
- {
- // Scientific format:
- if(showpoint || (str.size() > 1))
- {
- str.insert(1U, 1U, '.');
- }
- str.append(1U, 'e');
- string_type e = std::to_string(std::abs(my_exp));
- if(e.size() < 2U)
- {
- e.insert(0U, 2U - e.size(), '0');
- }
- if(my_exp < 0)
- {
- e.insert(0U, 1U, '-');
- }
- else
- {
- e.insert(0U, 1U, '+');
- }
- str.append(e);
- }
- if(b_neg)
- {
- str.insert(0U, 1U, '-');
- }
- else if(showpos)
- {
- str.insert(0U, 1U, '+');
- }
- }
- template<class float_type, class type_a> inline void eval_convert_to(type_a* pa, const float_type& cb) { *pa = static_cast<type_a>(cb); }
- template<class float_type, class type_a> inline void eval_add (float_type& b, const type_a& a) { b += a; }
- template<class float_type, class type_a> inline void eval_subtract (float_type& b, const type_a& a) { b -= a; }
- template<class float_type, class type_a> inline void eval_multiply (float_type& b, const type_a& a) { b *= a; }
- template<class float_type> inline void eval_multiply (float_type& b, const float_type& cb, const float_type& cb2) { b = (cb * cb2); }
- template<class float_type, class type_a> inline void eval_divide (float_type& b, const type_a& a) { b /= a; }
- template<class float_type> inline void eval_log10 (float_type& b, const float_type& cb) { b = std::log10(cb); }
- template<class float_type> inline void eval_floor (float_type& b, const float_type& cb) { b = std::floor(cb); }
- inline void round_string_up_at(std::string& s, int pos, int& expon)
- {
- // This subroutine rounds up a string representation of a
- // number at the given position pos.
- if(pos < 0)
- {
- s.insert(0U, 1U, '1');
- s.erase(s.size() - 1U);
- ++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 float_type>
- std::string convert_to_string(float_type& x,
- std::streamsize digits,
- const std::ios_base::fmtflags f)
- {
- const bool isneg = (x < 0);
- const bool iszero = ((!isneg) ? bool(+x < (std::numeric_limits<float_type>::min)())
- : bool(-x < (std::numeric_limits<float_type>::min)()));
- const bool isnan = (x != x);
- const bool isinf = ((!isneg) ? bool(+x > (std::numeric_limits<float_type>::max)())
- : bool(-x > (std::numeric_limits<float_type>::max)()));
- int expon = 0;
- if(digits <= 0) { digits = std::numeric_limits<float_type>::max_digits10; }
- const int org_digits = static_cast<int>(digits);
- std::string result;
- if(iszero)
- {
- result = "0";
- }
- else if(isinf)
- {
- if(x < 0)
- {
- return "-inf";
- }
- else
- {
- return ((f & std::ios_base::showpos) == std::ios_base::showpos) ? "+inf" : "inf";
- }
- }
- else if(isnan)
- {
- return "nan";
- }
- else
- {
- // Start by figuring out the base-10 exponent.
- if(isneg) { x = -x; }
- float_type t;
- constexpr float_type ten = 10;
- eval_log10(t, x);
- eval_floor(t, t);
- eval_convert_to(&expon, t);
- if(-expon > std::numeric_limits<float_type>::max_exponent10 - 3)
- {
- int e = -expon / 2;
- const float_type t2 = boost::math::cstdfloat::detail::pown(ten, e);
- eval_multiply(t, t2, x);
- eval_multiply(t, t2);
- if((expon & 1) != 0)
- {
- eval_multiply(t, ten);
- }
- }
- else
- {
- t = boost::math::cstdfloat::detail::pown(ten, -expon);
- eval_multiply(t, x);
- }
- // Make sure that the value lies between [1, 10), and adjust if not.
- if(t < 1)
- {
- eval_multiply(t, 10);
- --expon;
- }
- else if(t >= 10)
- {
- eval_divide(t, 10);
- ++expon;
- }
- float_type digit;
- int 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 base-10 digits one at a time.
- for(int 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);
- }
- if (result.size() == 0)
- result = "0";
- // Possibly round the result.
- if(digits >= 0)
- {
- eval_floor(digit, t);
- eval_convert_to(&cdigit, digit);
- eval_subtract(t, digit);
- if((cdigit == 5) && (t == 0))
- {
- // Use simple bankers rounding.
- if((static_cast<int>(*result.rbegin() - '0') & 1) != 0)
- {
- round_string_up_at(result, static_cast<int>(result.size() - 1U), expon);
- if (digits == 0) digits = 1;
- }
- }
- else if(cdigit >= 5)
- {
- round_string_up_at(result, static_cast<int>(result.size() - 1u), expon);
- if (digits == 0) digits = 1;
- }
- }
- }
- while((result.size() > static_cast<std::string::size_type>(digits)) && result.size())
- {
- // We may get here as a result of rounding.
- if(result.size() > 1U)
- {
- result.erase(result.size() - 1U);
- }
- else
- {
- if(expon > 0)
- {
- --expon; // so we put less padding in the result.
- }
- else
- {
- ++expon;
- }
- ++digits;
- }
- }
- if(isneg)
- {
- result.insert(0U, 1U, '-');
- }
- format_float_string(result, expon, org_digits, f, iszero);
- return result;
- }
- template <class float_type>
- bool convert_from_string(float_type& value, const char* p)
- {
- value = 0;
- if((p == nullptr) || (*p == '\0'))
- {
- return false;
- }
- bool is_neg = false;
- bool is_neg_expon = false;
- constexpr int ten = 10;
- int expon = 0;
- int digits_seen = 0;
- constexpr int max_digits = std::numeric_limits<float_type>::max_digits10 + 1;
- if(*p == '+')
- {
- ++p;
- }
- else if(*p == '-')
- {
- is_neg = true;
- ++p;
- }
- const bool isnan = ((std::strcmp(p, "nan") == 0) || (std::strcmp(p, "NaN") == 0) || (std::strcmp(p, "NAN") == 0));
- if(isnan)
- {
- eval_divide(value, 0);
- if(is_neg)
- {
- value = -value;
- }
- return true;
- }
- const bool isinf = ((std::strcmp(p, "inf") == 0) || (std::strcmp(p, "Inf") == 0) || (std::strcmp(p, "INF") == 0));
- if(isinf)
- {
- value = 1;
- eval_divide(value, 0);
- if(is_neg)
- {
- value = -value;
- }
- return true;
- }
- // Grab all the leading digits before the decimal point.
- while(std::isdigit(*p))
- {
- eval_multiply(value, ten);
- eval_add(value, static_cast<int>(*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(value, ten);
- eval_add(value, static_cast<int>(*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;
- }
- int 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 parts.
- float_type t;
- t = ten;
- if(expon > (std::numeric_limits<float_type>::min_exponent10 + 2))
- {
- t = boost::math::cstdfloat::detail::pown(t, expon);
- eval_multiply(value, t);
- }
- else
- {
- t = boost::math::cstdfloat::detail::pown(t, (expon + digits_seen + 1));
- eval_multiply(value, t);
- t = ten;
- t = boost::math::cstdfloat::detail::pown(t, (-digits_seen - 1));
- eval_multiply(value, t);
- }
- }
- if(is_neg)
- {
- value = -value;
- }
- return (*p == '\0');
- }
- } } } } // boost::math::cstdfloat::detail
- namespace std
- {
- template<typename char_type, class traits_type>
- inline std::basic_ostream<char_type, traits_type>& operator<<(std::basic_ostream<char_type, traits_type>& os, const boost::math::cstdfloat::detail::float_internal128_t& x)
- {
- boost::math::cstdfloat::detail::float_internal128_t non_const_x = x;
- const std::string str = boost::math::cstdfloat::detail::convert_to_string(non_const_x,
- os.precision(),
- os.flags());
- std::basic_ostringstream<char_type, traits_type> ostr;
- ostr.flags(os.flags());
- ostr.imbue(os.getloc());
- ostr.precision(os.precision());
- static_cast<void>(ostr << str);
- return (os << ostr.str());
- }
- template<typename char_type, class traits_type>
- inline std::basic_istream<char_type, traits_type>& operator>>(std::basic_istream<char_type, traits_type>& is, boost::math::cstdfloat::detail::float_internal128_t& x)
- {
- std::string str = boost::math::detail::read_string_while(is, "+-eE.0123456789infINFnanNANinfinityINFINITY");
- const bool conversion_is_ok = boost::math::cstdfloat::detail::convert_from_string(x, str.c_str());
- if(false == conversion_is_ok)
- {
- for(std::string::const_reverse_iterator it = str.rbegin(); it != str.rend(); ++it)
- {
- static_cast<void>(is.putback(*it));
- }
- is.setstate(ios_base::failbit);
- BOOST_MATH_THROW_EXCEPTION(std::runtime_error("Unable to interpret input string as a boost::float128_t"));
- }
- return is;
- }
- }
- #endif // Use __GNUC__ or __INTEL_COMPILER libquadmath
- #endif // Not BOOST_CSTDFLOAT_NO_LIBQUADMATH_SUPPORT (i.e., the user would like to have libquadmath support)
- #endif // BOOST_MATH_CSTDFLOAT_IOSTREAM_2014_02_15_HPP_
|