From cf5b72fa530fc73416ede8bcbcda2c679942fceb Mon Sep 17 00:00:00 2001 From: Christopher Kormanyos Date: Mon, 26 Jul 2021 19:42:38 +0200 Subject: [PATCH] Boost-ified draft ok but GCC long double broke --- .../boost/multiprecision/cpp_double_float.hpp | 751 +++++++----------- test/test_cpp_double_float_arithmetic.cpp | 44 +- 2 files changed, 327 insertions(+), 468 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_float.hpp b/include/boost/multiprecision/cpp_double_float.hpp index 32d8d27b..e65efc3e 100644 --- a/include/boost/multiprecision/cpp_double_float.hpp +++ b/include/boost/multiprecision/cpp_double_float.hpp @@ -38,12 +38,34 @@ template inline cpp_double_float template inline cpp_double_float operator*(const cpp_double_float& a, const FloatingPointType& b); template inline cpp_double_float operator/(const cpp_double_float& a, const FloatingPointType& b); +template inline bool operator< (const cpp_double_float& a, const cpp_double_float& b); +template inline bool operator<=(const cpp_double_float& a, const cpp_double_float& b); +template inline bool operator==(const cpp_double_float& a, const cpp_double_float& b); +template inline bool operator!=(const cpp_double_float& a, const cpp_double_float& b); +template inline bool operator>=(const cpp_double_float& a, const cpp_double_float& b); +template inline bool operator> (const cpp_double_float& a, const cpp_double_float& b); + +template void eval_add (cpp_double_float& result, const cpp_double_float& x); +template void eval_subtract(cpp_double_float& result, const cpp_double_float& x); +template void eval_multiply(cpp_double_float& result, const cpp_double_float& x); +template void eval_divide (cpp_double_float& result, const cpp_double_float& x); + +template +std::basic_ostream& operator<<(std::basic_ostream& os, + const cpp_double_float& f); } } } // namespace boost::multiprecision::backends -// Foward decleration for std::numeric_limits -template -class std::numeric_limits >; +namespace std { + +// Foward declaration of std::numeric_limits +template +class numeric_limits, ExpressionTemplatesOption>>; + +} namespace boost { namespace multiprecision { @@ -86,9 +108,10 @@ class cpp_double_float using float_type = FloatingPointType; using rep_type = std::pair; - using signed_types = std::tuple< signed char, signed short, signed int, signed long, signed long long, std::intmax_t>; - using unsigned_types = std::tuple; - using float_types = std::tuple; + using signed_types = std::tuple< signed char, signed short, signed int, signed long, signed long long, std::intmax_t>; + using unsigned_types = std::tuple; + using float_types = std::tuple; + using exponent_type = int; // Default constructor. cpp_double_float() { } @@ -177,6 +200,11 @@ class cpp_double_float set_str(str); } + cpp_double_float(const char* pstr) + { + set_str(std::string(pstr)); + } + constexpr cpp_double_float(cpp_double_float&&) = default; ~cpp_double_float() = default; @@ -392,12 +420,251 @@ class cpp_double_float return result; } - static void normalize_pair(std::pair& p, bool fast = true) + static void normalize_pair(rep_type& p, bool fast = true) { // Convert a pair of floats to standard form p = (fast ? fast_exact_sum(p.first, p.second) : exact_sum(p.first, p.second)); } + void swap(cpp_double_float& other) + { + rep_type tmp = data; + + data = other.data; + + other.data = tmp; + } + + int compare(const cpp_double_float& other) const + { + // Return 1 for *this > other, -1 for *this < other, 0 for *this = other. + int n_result; + + if ((first() > other.first()) || ((first() == other.first()) && (second() > other.second()))) { n_result = 1; } + else if((first() < other.first()) || ((first() == other.first()) && (second() < other.second()))) { n_result = -1; } + else { n_result = 0; } + + return n_result; + } + + std::string str(std::streamsize number_of_digits, const std::ios::fmtflags format_flags) const + { + std::stringstream strm; + + strm.flags (format_flags); + strm.imbue (strm.getloc()); + strm.precision(number_of_digits); + + using std::fabs; + auto is_set = [&](std::ios::fmtflags flg) { + return strm.flags() & flg; + }; + + using std::isinf; + using std::floor; + using std::log10; + if (isinf(first())) + { + strm << first(); + return strm.str(); + } + + if (*this < cpp_double_float(0) || strm.flags() & std::ios::showpos) + strm << (*this < cpp_double_float(0) ? "-" : "+"); + + int exp10 = 0; + + if (*this != cpp_double_float(0)) + exp10 = (int)floor(log10(fabs(first()))); + else + exp10 = 0; + + cpp_double_float f_prime = (*this > cpp_double_float(0) ? *this : -*this); + f_prime /= cpp_double_float::pow10(exp10); + + // TODO Handle subnormal numbers + + if (f_prime < cpp_double_float(1) && f_prime > cpp_double_float(0)) + { + f_prime *= FloatingPointType(10); + exp10++; + } + else if (f_prime >= cpp_double_float(10)) + { + f_prime /= FloatingPointType(10); + exp10--; + } + + // Collect all the required digits to print (plus one digit for rounding) + std::vector digits; + + int p = (int)strm.precision(); + if (is_set(std::ios::fixed)) + p += exp10 + 1; + else if (is_set(std::ios::scientific)) + p = (std::max)(1, p + 1); + else + p = (std::max)(p, 1); + + // TODO Maybe switch to fmod() based digit extraction for correct rounding? + while (p-- > 0) + { + // FIXME Replace with std::floor function + int digit = static_cast(f_prime.first()); + + if (f_prime.second() < 0 && (f_prime.first() - (FloatingPointType)digit < -f_prime.second())) + digit -= 1; + + BOOST_ASSERT(digit >= 0 && digit <= 9); + + digits.push_back(digit); + + f_prime -= static_cast(digit); + f_prime *= static_cast(10); + } + + auto round_up = [&]() { + int i = (int) digits.size() - 1; + if (i > -1) + { + do + { + if (digits[i] == 9) + digits[i--] = 0; + else + { + digits[i--] += 1; + break; + } + } while (i >= 0); + + // Special case in which all of the collected digits are incorrectly + // rounded (e.g. 9.999 rounded to three significant figures = 10.0) + if (i == -1 && digits[0] == 0) + { + digits = {1}; + exp10++; + } + } + else + { + digits.insert(digits.begin(), 1); + exp10++; + } + }; + + // Perform rounding (rounding mode = round-to-nearest, ties-to-even) + // Three possible cases: the remaining part of the number is + // (1) greater than 0.5 (round-up) + // (2) less than 0.5 (round-down) + // (3) equal to 0.5 (round-to-even) + if (f_prime > cpp_double_float(5)) + round_up(); + else if (f_prime < cpp_double_float(5)) // do nothing. already correctly rounded + { + // TODO add some kind of an option for configurable rounding mode + } + else if (digits.back() % 2 != 0) + // remaining part is exactly 0.5, so round-to-even + round_up(); + + // Remove trailing zeroes + if (!is_set(std::ios::fixed) && !is_set(std::ios::scientific) && !is_set(std::ios::showpoint)) + while (digits.back() == 0 && (std::ptrdiff_t(digits.size()) > std::ptrdiff_t(1 + exp10) || exp10 < 0)) + digits.pop_back(); + + auto fill_zeroes = [](std::string& s, size_t pos, int n) { + for (int i = 0; i < n; ++i) + s.insert(pos, "0"); + }; + + // Print the required numbers to a string + std::string str = ""; + size_t str_size; + + for (auto d : digits) + str.push_back(static_cast(d + '0')); + + // Fixed-point style + if (is_set(std::ios::fixed) || (exp10 >= -4 && (exp10 < strm.precision()) && !is_set(std::ios::scientific))) + { + if (exp10 + 1 <= 0) // Number < 1 + { + str_size = (size_t)strm.precision() + 2; + + if (!is_set(std::ios::fixed) && strm.precision() == 0) + str_size++; + + str.insert(0, "0."); + + fill_zeroes(str, 2, -(exp10 + 1)); + if (!is_set(std::ios::fixed)) + str_size += -(exp10 + 1); + } + else // Number >= 1 + { + str_size = std::size_t(1 + strm.precision()); + if (is_set(std::ios::fixed)) + str_size += exp10 + 1; + + fill_zeroes(str, str.size(), (int) str_size - (int) str.size() - 1); + + BOOST_ASSERT(std::ptrdiff_t(exp10 + 1) <= std::ptrdiff_t(str.size())); + str.insert(exp10 + 1, "."); + } + + while (str.size() > str_size) + str.pop_back(); + while (str.size() < str_size) + str.push_back('0'); + while (!is_set(std::ios::showpoint) && !is_set(std::ios::fixed) && str.back() == '0') + str.pop_back(); + + if (str.back() == '.' && !is_set(std::ios::showpoint)) + str.pop_back(); + } + // Scientific style + else if (is_set(std::ios::scientific) || (exp10 < -4 || (exp10 + 1 > (std::max)((int)strm.precision(), 1)))) + { + str_size = (size_t)strm.precision() + 1; + if (strm.precision() == 0 || is_set(std::ios::scientific)) + str_size++; + + str.insert(1, "."); + // Pad with zeroes + fill_zeroes(str, str.size(), (int) str_size - (int) str.size()); + + // Remove trailing zeroes + while (str.size() > str_size) + str.pop_back(); + while (!is_set(std::ios::scientific) && !is_set(std::ios::showpoint) && str.back() == '0') + str.pop_back(); + + // Remove unnecessary point + if (str.back() == '.' && !is_set(std::ios::showpoint)) + str.pop_back(); + + std::stringstream ss; + + ss << str; + ss << (strm.flags() & std::ios::uppercase ? "E" : "e"); + ss << (exp10 < 0 ? "-" : "+"); + using std::log10; + ss.width((std::max)(1 + (std::streamsize)log10(exp10), (std::streamsize)2)); + ss.fill('0'); + ss << fabs(exp10); + + str = ss.str(); + } + else if (exp10 == strm.precision()) + { + if (strm.flags() & std::ios::showpoint) + str.push_back('.'); + } + + return strm.str(); + } + private: rep_type data; @@ -503,6 +770,13 @@ template inline cpp_double_float template inline cpp_double_float operator*(const cpp_double_float& a, const cpp_double_float& b) { return cpp_double_float(a) *= b; } template inline cpp_double_float operator/(const cpp_double_float& a, const cpp_double_float& b) { return cpp_double_float(a) /= b; } +template inline bool operator< (const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) < 0); } +template inline bool operator<=(const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) <= 0); } +template inline bool operator==(const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) == 0); } +template inline bool operator!=(const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) != 0); } +template inline bool operator>=(const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) >= 0); } +template inline bool operator> (const cpp_double_float& a, const cpp_double_float& b) { return (a.compare(b) > 0); } + // -- String Conversions // FIXME Merge set_str() to operator<< template @@ -563,455 +837,14 @@ inline void cpp_double_float::set_str(std::string str) // more detail, and then comparing. Some minor complications arise while // comparing an unsigned type to cpp_double_float<> that are handled as well -// operator> -template -inline constexpr typename std::enable_if::value, bool>::type -operator>(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? false // Check for negative values - : larger_type(a) > larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() > b.first() ? true : a.first() == b.first() ? (a.second() > b.second() ? true : false) : false; -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) > static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>(const ComparisionType& a, const cpp_double_float& b) -{ - return b < a; -} - -// operator< -template -inline constexpr typename std::enable_if::value, bool>::type -operator<(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? true // Check for negative values - : larger_type(a) < larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() < b.first() ? true : (a.first() == b.first()) ? (a.second() < b.second() ? true : false) : false; -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) < static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<(const ComparisionType& a, const cpp_double_float& b) -{ - return b > a; -} - -// operator>= -template -inline constexpr typename std::enable_if::value, bool>::type -operator>=(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? false // Check for negative values - : larger_type(a) >= larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>=(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() > b.first() ? true : (a.first() == b.first()) ? (a.second() >= b.second() ? true : false) : false; -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>=(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) >= static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator>=(const ComparisionType& a, const cpp_double_float& b) -{ - return b <= a; -} - -// operator <= -template -inline constexpr typename std::enable_if::value, bool>::type -operator<=(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? true // Check for negative values - : larger_type(a) <= larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<=(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() < b.first() ? true : (a.first() == b.first()) ? (a.second() <= b.second() ? true : false) : false; -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<=(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) <= static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator<=(const ComparisionType& a, const cpp_double_float& b) -{ - return b >= a; -} - -// operator == -template -inline constexpr typename std::enable_if::value, bool>::type -operator==(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? false // Check for negative values - : larger_type(a) == larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator==(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() == b.first() ? (a.second() == b.second() ? true : false) : false; -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator==(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) == static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator==(const ComparisionType& a, const cpp_double_float& b) -{ - return b == a; -} - -// operator != -template -inline constexpr typename std::enable_if::value, bool>::type -operator!=(const cpp_double_float& a, const ComparisionType& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return ( (std::is_unsigned::value && std::is_same::value) - && (a.is_negative())) ? true // Check for negative values - : larger_type(a) != larger_type(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator!=(const cpp_double_float& a, const cpp_double_float& b) -{ - return a.first() != b.first() ? true : (a.second() != b.second() ? true : false); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator!=(const cpp_double_float& a, const cpp_double_float& b) -{ - using first_type = typename std::remove_reference::type; - using second_type = typename std::remove_reference::type; - using larger_type = typename std::conditional::digits >= std::numeric_limits::digits, first_type, second_type>::type; - - return static_cast(a) != static_cast(b); -} - -template -inline constexpr typename std::enable_if::value, bool>::type -operator!=(const ComparisionType& a, const cpp_double_float& b) -{ - return b != a; -} -// -- - // -- Input/Output Streaming template std::basic_ostream& operator<<(std::basic_ostream& os, const cpp_double_float& f) { - using std::fabs; - auto is_set = [&](std::ios::fmtflags flg) { - return os.flags() & flg; - }; + const std::string str_result = f.str(os.precision(), os.flags()); - using std::isinf; - using std::floor; - using std::log10; - if (isinf(f.first())) - { - os << f.first(); - return os; - } - - if (f < cpp_double_float(0) || os.flags() & std::ios::showpos) - os << (f < cpp_double_float(0) ? "-" : "+"); - - int exp10 = 0; - - if (f != cpp_double_float(0)) - exp10 = (int)floor(log10(fabs(f.first()))); - else - exp10 = 0; - - cpp_double_float f_prime = (f > cpp_double_float(0) ? f : -f); - f_prime /= cpp_double_float::pow10(exp10); - - // TODO Handle subnormal numbers - - if (f_prime < cpp_double_float(1) && f_prime > cpp_double_float(0)) - { - f_prime *= FloatingPointType(10); - exp10++; - } - else if (f_prime >= 10) - { - f_prime /= FloatingPointType(10); - exp10--; - } - - // Collect all the required digits to print (plus one digit for rounding) - std::vector digits; - - int p = (int)os.precision(); - if (is_set(std::ios::fixed)) - p += exp10 + 1; - else if (is_set(std::ios::scientific)) - p = (std::max)(1, p + 1); - else - p = (std::max)(p, 1); - - // TODO Maybe switch to fmod() based digit extraction for correct rounding? - while (p-- > 0) - { - // FIXME Replace with std::floor function - int digit = static_cast(f_prime.first()); - - if (f_prime.second() < 0 && (f_prime.first() - (FloatingPointType)digit < -f_prime.second())) - digit -= 1; - - BOOST_ASSERT(digit >= 0 && digit <= 9); - - digits.push_back(digit); - - f_prime -= static_cast(digit); - f_prime *= static_cast(10); - } - - auto round_up = [&]() { - int i = (int) digits.size() - 1; - if (i > -1) - { - do - { - if (digits[i] == 9) - digits[i--] = 0; - else - { - digits[i--] += 1; - break; - } - } while (i >= 0); - - // Special case in which all of the collected digits are incorrectly - // rounded (e.g. 9.999 rounded to three significant figures = 10.0) - if (i == -1 && digits[0] == 0) - { - digits = {1}; - exp10++; - } - } - else - { - digits.insert(digits.begin(), 1); - exp10++; - } - }; - - // Perform rounding (rounding mode = round-to-nearest, ties-to-even) - // Three possible cases: the remaining part of the number is - // (1) greater than 0.5 (round-up) - // (2) less than 0.5 (round-down) - // (3) equal to 0.5 (round-to-even) - if (f_prime > 5) - round_up(); - else if (f_prime < 5) // do nothing. already correctly rounded - { - // TODO add some kind of an option for configurable rounding mode - } - else if (digits.back() % 2 != 0) - // remaining part is exactly 0.5, so round-to-even - round_up(); - - // Remove trailing zeroes - if (!is_set(std::ios::fixed) && !is_set(std::ios::scientific) && !is_set(std::ios::showpoint)) - while (digits.back() == 0 && (std::ptrdiff_t(digits.size()) > std::ptrdiff_t(1 + exp10) || exp10 < 0)) - digits.pop_back(); - - auto fill_zeroes = [](std::string& s, size_t pos, int n) { - for (int i = 0; i < n; ++i) - s.insert(pos, "0"); - }; - - // Print the required numbers to a string - std::string str = ""; - size_t str_size; - - for (auto d : digits) - str.push_back(static_cast(d + '0')); - - // Fixed-point style - if (is_set(std::ios::fixed) || (exp10 >= -4 && (exp10 < os.precision()) && !is_set(std::ios::scientific))) - { - if (exp10 + 1 <= 0) // Number < 1 - { - str_size = (size_t)os.precision() + 2; - - if (!is_set(std::ios::fixed) && os.precision() == 0) - str_size++; - - str.insert(0, "0."); - - fill_zeroes(str, 2, -(exp10 + 1)); - if (!is_set(std::ios::fixed)) - str_size += -(exp10 + 1); - } - else // Number >= 1 - { - str_size = std::size_t(1 + os.precision()); - if (is_set(std::ios::fixed)) - str_size += exp10 + 1; - - fill_zeroes(str, str.size(), (int) str_size - (int) str.size() - 1); - - BOOST_ASSERT(std::ptrdiff_t(exp10 + 1) <= std::ptrdiff_t(str.size())); - str.insert(exp10 + 1, "."); - } - - while (str.size() > str_size) - str.pop_back(); - while (str.size() < str_size) - str.push_back('0'); - while (!is_set(std::ios::showpoint) && !is_set(std::ios::fixed) && str.back() == '0') - str.pop_back(); - - if (str.back() == '.' && !is_set(std::ios::showpoint)) - str.pop_back(); - } - // Scientific style - else if (is_set(std::ios::scientific) || (exp10 < -4 || (exp10 + 1 > (std::max)((int)os.precision(), 1)))) - { - str_size = (size_t)os.precision() + 1; - if (os.precision() == 0 || is_set(std::ios::scientific)) - str_size++; - - str.insert(1, "."); - // Pad with zeroes - fill_zeroes(str, str.size(), (int) str_size - (int) str.size()); - - // Remove trailing zeroes - while (str.size() > str_size) - str.pop_back(); - while (!is_set(std::ios::scientific) && !is_set(std::ios::showpoint) && str.back() == '0') - str.pop_back(); - - // Remove unnecessary point - if (str.back() == '.' && !is_set(std::ios::showpoint)) - str.pop_back(); - - std::stringstream ss; - - ss << str; - ss << (os.flags() & std::ios::uppercase ? "E" : "e"); - ss << (exp10 < 0 ? "-" : "+"); - using std::log10; - ss.width((std::max)(1 + (std::streamsize)log10(exp10), (std::streamsize)2)); - ss.fill('0'); - ss << fabs(exp10); - - str = ss.str(); - } - else if (exp10 == os.precision()) - { - if (os.flags() & std::ios::showpoint) - str.push_back('.'); - } - - os << str; - return os; + return (os << str_result); } template @@ -1024,17 +857,26 @@ operator>>(std::basic_istream& is, cpp_double_float void eval_add (cpp_double_float& result, const cpp_double_float& x) { result += x; } +template void eval_subtract(cpp_double_float& result, const cpp_double_float& x) { result -= x; } +template void eval_multiply(cpp_double_float& result, const cpp_double_float& x) { result *= x; } +template void eval_divide (cpp_double_float& result, const cpp_double_float& x) { result /= x; } + } } } // namespace boost::multiprecision::backends +namespace std { + // Specialization of numeric_limits for cpp_double_float<> -template -class std::numeric_limits> +template +class numeric_limits, ExpressionTemplatesOption>> : public std::numeric_limits { private: using base_class_type = std::numeric_limits; - using self_type = boost::multiprecision::backends::cpp_double_float; + using self_type = + boost::multiprecision::number, ExpressionTemplatesOption>; public: static constexpr bool is_iec559 = false; @@ -1056,6 +898,7 @@ public: static constexpr self_type quiet_NaN () noexcept { return self_type( base_class_type::quiet_NaN()); } static constexpr self_type signaling_NaN() noexcept { return self_type( base_class_type::signaling_NaN()); } }; -// TODO have explicit specializations for cpp_double_float< float/double > + +} #endif // BOOST_MP_CPP_DOUBLE_FLOAT_2021_06_05_HPP diff --git a/test/test_cpp_double_float_arithmetic.cpp b/test/test_cpp_double_float_arithmetic.cpp index dfbc963c..0a3fb885 100644 --- a/test/test_cpp_double_float_arithmetic.cpp +++ b/test/test_cpp_double_float_arithmetic.cpp @@ -31,6 +31,16 @@ #include #include +#if defined(__clang__) + #if defined __has_feature && __has_feature(thread_sanitizer) + #define CPP_DOUBLE_FLOAT_REDUCE_TEST_DEPTH + #endif +#elif defined(__GNUC__) + #if defined(__SANITIZE_THREAD__) + #define CPP_DOUBLE_FLOAT_REDUCE_TEST_DEPTH + #endif +#endif + namespace local { template @@ -41,7 +51,7 @@ struct control static constexpr int max_digits10 = int(float(digits) * 0.301F) + 2; static constexpr int max_exponent10 = std::numeric_limits::max_exponent10; - using double_float_type = boost::multiprecision::backends::cpp_double_float; + using double_float_type = boost::multiprecision::number, boost::multiprecision::et_off>; using control_float_type = boost::multiprecision::number::digits10) + 1>, boost::multiprecision::et_off>; using random_engine_type = std::linear_congruential_engine; @@ -164,8 +174,8 @@ bool test_op(char op, const unsigned count = 10000U) const double_float_type df_a(str_a); const double_float_type df_b(str_b); - const control_float_type ctrl_a = control_float_type(df_a.first()) + control_float_type(df_a.second()); - const control_float_type ctrl_b = control_float_type(df_b.first()) + control_float_type(df_b.second()); + const control_float_type ctrl_a = control_float_type(double_float_type::canonical_value(df_a).first()) + control_float_type(double_float_type::canonical_value(df_a).second()); + const control_float_type ctrl_b = control_float_type(double_float_type::canonical_value(df_b).first()) + control_float_type(double_float_type::canonical_value(df_b).second()); double_float_type df_c; control_float_type ctrl_c; @@ -198,7 +208,7 @@ bool test_op(char op, const unsigned count = 10000U) break; } - control_float_type ctrl_df_c = control_float_type(control_float_type(df_c.first()) + control_float_type(df_c.second())); + control_float_type ctrl_df_c = control_float_type(control_float_type(double_float_type::canonical_value(df_c).first()) + control_float_type(double_float_type::canonical_value(df_c).second())); const control_float_type delta = fabs(1 - fabs(ctrl_c / ctrl_df_c)); @@ -207,12 +217,6 @@ bool test_op(char op, const unsigned count = 10000U) std::cerr << std::setprecision(std::numeric_limits::digits10 + 2); std::cerr << " [FAILED] while performing '" << std::setprecision(100000) << ctrl_a << "' " << op << " '" << ctrl_b << "', got incorrect result: " << (df_c) << std::endl; - // uncomment for more debugging information (only for cpp_double_float<> type) - //std::cerr << "(df_a = " << df_a.get_raw_str() << ", df_b = " << df_b.get_raw_str() << ")" << std::endl; - //std::cerr << "expected: " << ctrl_c << std::endl; - //std::cerr << "actual : " << ctrl_df_c << " (" << df_c.get_raw_str() << ")" << std::endl; - //std::cerr << "error : " << delta << std::endl; - return false; } } @@ -245,11 +249,23 @@ int main() { bool result_is_ok = true; - result_is_ok &= local::test_arithmetic (1000000U); - result_is_ok &= local::test_arithmetic(1000000U); - //result_is_ok &= local::test_arithmetic(); + #if defined(CPP_DOUBLE_FLOAT_REDUCE_TEST_DEPTH) + constexpr unsigned int test_cases_built_in = 10000U; + #else + constexpr unsigned int test_cases_built_in = 1000000U; + #endif + + #if defined(CPP_DOUBLE_FLOAT_REDUCE_TEST_DEPTH) + constexpr unsigned int test_cases_float128 = 500U; + #else + constexpr unsigned int test_cases_float128 = 10000U; + #endif + + result_is_ok &= local::test_arithmetic (test_cases_built_in); + result_is_ok &= local::test_arithmetic (test_cases_built_in); + //result_is_ok &= local::test_arithmetic(test_cases_built_in); #ifdef BOOST_MATH_USE_FLOAT128 - result_is_ok &= local::test_arithmetic(20000U); + result_is_ok &= local::test_arithmetic(test_cases_float128); #endif return (result_is_ok ? 0 : -1);