/////////////////////////////////////////////////////////////////////////////// // 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 #include #include #if defined(BOOST_CSTDFLOAT_NO_LIBQUADMATH_CMATH) #error You can not use 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 #include #include #include #include #include #include #include #include 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(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 inline std::basic_ostream& operator<<(std::basic_ostream& os, const boost::math::cstdfloat::detail::float_internal128_t& x) { std::basic_ostringstream ostr; ostr.flags(os.flags()); ostr.imbue(os.getloc()); ostr.precision(os.precision()); char my_buffer[64U]; const int my_prec = static_cast(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(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(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(ostr << my_buffer2); delete [] my_buffer2; } else { static_cast(ostr << my_buffer); } return (os << ostr.str()); } template inline std::basic_istream& operator>>(std::basic_istream& 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(p_end - str.c_str()) != static_cast(str.length())) { for(std::string::const_reverse_iterator it = str.rbegin(); it != str.rend(); ++it) { static_cast(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 #include namespace boost { namespace math { namespace cstdfloat { namespace detail { template 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((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(str.size()); chars = digits - chars; if(scientific) { ++chars; } if(chars > 0) { str.append(static_cast(chars), '0'); } } if(fixed || (!scientific && (my_exp >= -4) && (my_exp < digits))) { if((1 + my_exp) > static_cast(str.size())) { // Just pad out the end with zeros. str.append(static_cast((1 + my_exp) - static_cast(str.size())), '0'); if(showpoint || fixed) { str.append("."); } } else if(my_exp + 1 < static_cast(str.size())) { if(my_exp < 0) { str.insert(0U, static_cast(-1 - my_exp), '0'); str.insert(0U, "0."); } else { // Insert the decimal point: str.insert(static_cast(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(str.find('.') + 1U); l = digits - (static_cast(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 inline void eval_convert_to(type_a* pa, const float_type& cb) { *pa = static_cast(cb); } template inline void eval_add (float_type& b, const type_a& a) { b += a; } template inline void eval_subtract (float_type& b, const type_a& a) { b -= a; } template inline void eval_multiply (float_type& b, const type_a& a) { b *= a; } template inline void eval_multiply (float_type& b, const float_type& cb, const float_type& cb2) { b = (cb * cb2); } template inline void eval_divide (float_type& b, const type_a& a) { b /= a; } template inline void eval_log10 (float_type& b, const float_type& cb) { b = std::log10(cb); } template 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 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::min)()) : bool(-x < (std::numeric_limits::min)())); const bool isnan = (x != x); const bool isinf = ((!isneg) ? bool(+x > (std::numeric_limits::max)()) : bool(-x > (std::numeric_limits::max)())); int expon = 0; if(digits <= 0) { digits = std::numeric_limits::max_digits10; } const int org_digits = static_cast(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::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('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(*result.rbegin() - '0') & 1) != 0) { round_string_up_at(result, static_cast(result.size() - 1U), expon); if (digits == 0) digits = 1; } } else if(cdigit >= 5) { round_string_up_at(result, static_cast(result.size() - 1u), expon); if (digits == 0) digits = 1; } } } while((result.size() > static_cast(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 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::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(*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(*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::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 inline std::basic_ostream& operator<<(std::basic_ostream& 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 ostr; ostr.flags(os.flags()); ostr.imbue(os.getloc()); ostr.precision(os.precision()); static_cast(ostr << str); return (os << ostr.str()); } template inline std::basic_istream& operator>>(std::basic_istream& 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(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_