From e9c7aa28cd5f5fdc1aa11b18f344d4fe3ea67d8b Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Thu, 3 Jul 2025 16:22:54 +0200 Subject: [PATCH 001/114] Obtain more code coverage --- README.md | 2 +- .../boost/multiprecision/cpp_dec_float.hpp | 28 +- .../boost/multiprecision/cpp_double_fp.hpp | 60 ++-- .../multiprecision/detail/default_ops.hpp | 9 +- test/test_exp.cpp | 3 +- test/test_log.cpp | 4 +- test/test_various_edges.cpp | 178 +++++++++++- test/test_various_edges_more.cpp | 256 ++++++++++++++++-- 8 files changed, 458 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index b1051d734..f76b58810 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ requiring extended range and precision. Multiprecision consists of a generic interface to the mathematics of large numbers as well as a selection of big number backends. -This includes interfaces to GMP, MPFR, MPIR and TomMath +These include interfaces to GMP, MPFR, MPIR and TomMath and also Multiprecision's own collection of Boost-licensed, header-only backends for integers, rationals, floats and complex-floats. diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index c9b958c06..df1a173bd 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -427,7 +427,6 @@ class cpp_dec_float cpp_dec_float& div_unsigned_long_long(const unsigned long long n); // Elementary primitives. - cpp_dec_float& calculate_inv(); cpp_dec_float& calculate_sqrt(); void negate() @@ -568,6 +567,9 @@ class cpp_dec_float static bool data_elem_is_non_nine_predicate(const std::uint32_t& d) { return (d != static_cast(cpp_dec_float::cpp_dec_float_elem_mask - 1)); } static bool char_is_nonzero_predicate(const char& c) { return (c != static_cast('0')); } + // Inversion. + cpp_dec_float& calculate_inv(); + void from_unsigned_long_long(const unsigned long long u); template & cpp_dec_float cpp_dec_float& cpp_dec_float::calculate_inv() { - // Compute the inverse of *this. - const bool b_neg = neg; - - neg = false; - - // Handle special cases like zero, inf and NaN. + // Handle the special case of zero. if (iszero()) { *this = inf(); - if (b_neg) - negate(); + return *this; } + // Handle the special cases of inf and NaN. + if ((isnan)()) { return *this; @@ -1242,14 +1240,12 @@ cpp_dec_float& cpp_dec_float x(*this); // Generate the initial estimate using division. diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 4e8245f18..9049e0bcc 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -163,9 +163,11 @@ struct number_category> : pub namespace backends { -// A cpp_double_fp_backend is represented by an unevaluated sum of two floating-point -// units (say a0 and a1) which satisfy |a1| <= (1 / 2) * ulp(a0). +// A cpp_double_fp_backend is represented by an unevaluated sum of two +// floating-point units, a0 and a1, which satisfy |a1| <= (1 / 2) * ulp(a0). // The type of the floating-point constituents should adhere to IEEE754. +// This class has been tested with floats having single-precision (4 byte), +// double-precision (8 byte) and quad precision (16 byte, such as GCC's __float128). template class cpp_double_fp_backend @@ -178,10 +180,10 @@ class cpp_double_fp_backend cpp_df_qf_detail::is_floating_point::value && bool { - (cpp_df_qf_detail::ccmath::numeric_limits::digits == 24) - || (cpp_df_qf_detail::ccmath::numeric_limits::digits == 53) - || (cpp_df_qf_detail::ccmath::numeric_limits::digits == 64) - || (cpp_df_qf_detail::ccmath::numeric_limits::digits == 113) + ((cpp_df_qf_detail::ccmath::numeric_limits::digits == 24) && std::numeric_limits::is_specialized && std::numeric_limits::is_iec559) + || ((cpp_df_qf_detail::ccmath::numeric_limits::digits == 53) && std::numeric_limits::is_specialized && std::numeric_limits::is_iec559) + || ((cpp_df_qf_detail::ccmath::numeric_limits::digits == 64) && std::numeric_limits::is_specialized && std::numeric_limits::is_iec559) + || (cpp_df_qf_detail::ccmath::numeric_limits::digits == 113) }, "Error: float_type does not fulfil the backend requirements of cpp_double_fp_backend" ); @@ -514,12 +516,10 @@ class cpp_double_fp_backend } } - const float_type xlo { data.second }; + const rep_type thi_tlo { arithmetic::two_sum(data.second, v.data.second) }; data = arithmetic::two_sum(data.first, v.data.first); - const rep_type thi_tlo { arithmetic::two_sum(xlo, v.data.second) }; - data = arithmetic::two_hilo_sum(data.first, data.second + thi_tlo.first); data = arithmetic::two_hilo_sum(data.first, thi_tlo.second + data.second); @@ -578,12 +578,10 @@ class cpp_double_fp_backend return *this; } - const float_type xlo { data.second }; + const rep_type thi_tlo { arithmetic::two_diff(data.second, v.data.second) }; data = arithmetic::two_diff(data.first, v.data.first); - const rep_type thi_tlo { arithmetic::two_diff(xlo, v.data.second) }; - data = arithmetic::two_hilo_sum(data.first, data.second + thi_tlo.first); data = arithmetic::two_hilo_sum(data.first, thi_tlo.second + data.second); @@ -993,7 +991,8 @@ class cpp_double_fp_backend constexpr cpp_double_fp_backend my_value_eps_constexpr { - cpp_df_qf_detail::ccmath::unsafe::ldexp(float_type { 1 }, int { 3 - my_digits }) + cpp_double_fp_backend(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) + * cpp_double_fp_backend(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) }; static_assert @@ -1613,13 +1612,15 @@ template ::value && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) < 16))>::type const*> constexpr auto eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x) -> void { - const int fpc { eval_fpclassify(x) }; - using double_float_type = cpp_double_fp_backend; + constexpr double_float_type one { 1 }; + + const int fpc { eval_fpclassify(x) }; + if (fpc == FP_ZERO) { - result = double_float_type { 1.0F }; + result = one; } else if (fpc != FP_NORMAL) { @@ -1751,7 +1752,7 @@ constexpr auto eval_exp(cpp_double_fp_backend& result, const if (b_neg) { - result = double_float_type(1U) / result; + result = one / result; } } } @@ -1761,13 +1762,15 @@ template ::value && (((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) >= 16) && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) <= 36)))>::type const*> constexpr auto eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x) -> void { - const int fpc { eval_fpclassify(x) }; - using double_float_type = cpp_double_fp_backend; + constexpr double_float_type one { 1 }; + + const int fpc { eval_fpclassify(x) }; + if (fpc == FP_ZERO) { - result = double_float_type(1); + result = one; } else if (fpc != FP_NORMAL) { @@ -1901,7 +1904,7 @@ constexpr auto eval_exp(cpp_double_fp_backend& result, const if (b_neg) { - result = double_float_type(1U) / result; + result = one / result; } } } @@ -1911,13 +1914,15 @@ template ::value && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) > 36))>::type const*> constexpr auto eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x) -> void { - const int fpc { eval_fpclassify(x) }; - using double_float_type = cpp_double_fp_backend; + constexpr double_float_type one { 1 }; + + const int fpc { eval_fpclassify(x) }; + if (fpc == FP_ZERO) { - result = double_float_type(1); + result = one; } else if (fpc != FP_NORMAL) { @@ -2012,16 +2017,17 @@ constexpr auto eval_exp(cpp_double_fp_backend& result, const // Series expansion of hypergeometric_0f0(; ; x). // For this high(er) digit count, a scaled argument with subsequent // Taylor series expansion is actually more precise than Pade approximation. - for (unsigned n = 2U; n < 64U; ++n) + for (unsigned n { 2U }; n < 64U; ++n) { x_pow_n_div_n_fact *= xh; + x_pow_n_div_n_fact /= typename double_float_type::float_type(n); int n_tol { }; eval_frexp(dummy, x_pow_n_div_n_fact, &n_tol); - if ((n > 4U) && (n_tol < -(double_float_type::my_digits - 4))) + if ((n > 4U) && (n_tol < -(double_float_type::my_digits - 2))) { break; } @@ -2051,7 +2057,7 @@ constexpr auto eval_exp(cpp_double_fp_backend& result, const if (b_neg) { - result = double_float_type(1U) / result; + result = one / result; } } } diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index d66fe4a16..6e1aeb354 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -499,7 +499,7 @@ inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if< !std::is_same::va T t; t = number::canonical_value(u); return t; -} +} // LCOV_EXCL_LINE template inline BOOST_MP_CXX14_CONSTEXPR const T& make_T(const T& t) { @@ -1803,6 +1803,9 @@ BOOST_MP_CXX14_CONSTEXPR void eval_karatsuba_sqrt(Backend& result, const Backend result = s; } +// LCOV_EXCL_START +// This is known tested in the test-file test_various_edges.cpp. + template void BOOST_MP_CXX14_CONSTEXPR eval_integer_sqrt_bitwise(B& s, B& r, const B& x) { @@ -1861,6 +1864,8 @@ void BOOST_MP_CXX14_CONSTEXPR eval_integer_sqrt_bitwise(B& s, B& r, const B& x) } while (g >= 0); } +// LCOV_EXCL_STOP + template BOOST_MP_CXX14_CONSTEXPR void eval_integer_sqrt(Backend& result, Backend& r, const Backend& x) { @@ -3867,7 +3872,7 @@ conj(const number& arg) using default_ops::eval_conj; eval_conj(result.backend(), arg.backend()); return result; -} +} // LCOV_EXCL_LINE template inline BOOST_MP_CXX14_CONSTEXPR detail::expression< diff --git a/test/test_exp.cpp b/test/test_exp.cpp index 776b6e395..6cc7f1d16 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -14,8 +14,9 @@ #pragma warning(disable : 4127) #endif +#include + #include -#include "test.hpp" #include #include diff --git a/test/test_log.cpp b/test/test_log.cpp index 3860dbdf3..87ada4634 100644 --- a/test/test_log.cpp +++ b/test/test_log.cpp @@ -13,9 +13,11 @@ #define _SCL_SECURE_NO_WARNINGS #endif +#include + #include + #include -#include "test.hpp" #if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_MPZ) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT) && !defined(TEST_CPP_DOUBLE_FLOAT) #define TEST_MPF_50 diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 9db4378cb..03363efc7 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -28,10 +28,7 @@ #include #include -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wfloat-equal" -#elif defined(__GNUC__) +#if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" #endif @@ -97,13 +94,13 @@ namespace local { delta = fabs(a - b); // LCOV_EXCL_LINE - result_is_ok = (delta < tol); // LCOV_EXCL_LINE + result_is_ok = (delta <= tol); // LCOV_EXCL_LINE } else { delta = fabs(1 - (a / b)); - result_is_ok = (delta < tol); + result_is_ok = (delta <= tol); } return result_is_ok; @@ -501,6 +498,103 @@ namespace local return result_is_ok; } + template + auto test_fdim_edge() -> bool + { + using float_type = FloatType; + + std::mt19937_64 gen { time_point() }; + + std::uniform_real_distribution + dist + ( + static_cast(-125.5L), + static_cast(+125.5L) + ); + + auto result_is_ok = true; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const float_type val_nrm = ::my_one() * static_cast(dist(gen)); + const float_type val_nan = std::numeric_limits::quiet_NaN() * static_cast(dist(gen)); + const float_type val_inf = std::numeric_limits::infinity() * static_cast(dist(gen)); + + const double flt_nrm = static_cast(val_nrm); + const double flt_nan = std::numeric_limits::quiet_NaN() * static_cast(dist(gen)); + const double flt_inf = std::numeric_limits::infinity() * static_cast(dist(gen)); + + using std::fdim; + using std::fpclassify; + + float_type result_fdim { fdim(val_nrm, val_nan) }; + bool result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_nrm, val_inf); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_nan, val_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_inf, val_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_INFINITE); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + // MP-type argument-a mixed with built-in argument-b. + result_fdim = fdim(val_nrm, flt_nan); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_nrm, flt_inf); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_nan, flt_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(val_inf, flt_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_INFINITE); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + // Built-in argument-a mixed with MP-type argument-b. + result_fdim = fdim(flt_nrm, val_nan); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(flt_nrm, val_inf); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(flt_nan, val_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_ZERO); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + + result_fdim = fdim(flt_inf, val_nrm); + result_fdim_is_ok = (fpclassify(result_fdim) == FP_INFINITE); + BOOST_TEST(result_fdim_is_ok); + result_is_ok = (result_fdim_is_ok && result_is_ok); + } + + return result_is_ok; + } + template auto test_sqrt_edge() -> bool { @@ -1523,10 +1617,72 @@ namespace local return result_is_ok; } + + auto test_sqrt_integral_and_constexpr() -> void + { + { + // Select some pseudo-random integers + + // Table[N[Exp[Pi EulerGamma*m], 36], {m, 1, 301, 100}] + // 6.13111418242260482895474318171556018 632831453348 + // 3.47920348866883608793309754527486715 109524826009*10^79 + // 1.97433232450131483625758030703105499 465632954349*10^158 + // 1.12036796360599148200087404494586705 923184966483*10^237 + + // N[Sqrt[613111418242260482895474318171556018], 50] + // N[Sqrt[347920348866883608793309754527486715], 50] + // N[Sqrt[197433232450131483625758030703105499], 50] + // N[Sqrt[112036796360599148200087404494586705], 50] + + // 7.8301431547722068635982391629827009581580426009374*10^17 + // 5.8984773362867438315481181521503243715373594761667*10^17 + // 4.4433459515339505424085647442953457083310383400346*10^17 + // 3.3471898117764273004961506045440891118909367547368*10^17 + + // 783014315477220686 + // 589847733628674383 + // 444334595153395054 + // 334718981177642730 + + constexpr boost::multiprecision::uint128_t un0 = boost::multiprecision::uint128_t(UINT64_C(613111418242260482)) * boost::multiprecision::uint128_t(UINT64_C(1000000000000000000)) + boost::multiprecision::uint128_t(UINT64_C(895474318171556018)); + constexpr boost::multiprecision::uint128_t un1 = boost::multiprecision::uint128_t(UINT64_C(347920348866883608)) * boost::multiprecision::uint128_t(UINT64_C(1000000000000000000)) + boost::multiprecision::uint128_t(UINT64_C(793309754527486715)); + constexpr boost::multiprecision::uint128_t un2 = boost::multiprecision::uint128_t(UINT64_C(197433232450131483)) * boost::multiprecision::uint128_t(UINT64_C(1000000000000000000)) + boost::multiprecision::uint128_t(UINT64_C(625758030703105499)); + constexpr boost::multiprecision::uint128_t un3 = boost::multiprecision::uint128_t(UINT64_C(112036796360599148)) * boost::multiprecision::uint128_t(UINT64_C(1000000000000000000)) + boost::multiprecision::uint128_t(UINT64_C(200087404494586705)); + + constexpr boost::multiprecision::uint128_t sqrt_un0(sqrt(un0)); + constexpr boost::multiprecision::uint128_t sqrt_un1(sqrt(un1)); + constexpr boost::multiprecision::uint128_t sqrt_un2(sqrt(un2)); + constexpr boost::multiprecision::uint128_t sqrt_un3(sqrt(un3)); + + static_assert(static_cast(sqrt_un0) == UINT64_C(783014315477220686), "Error in constexpr integer square root"); + static_assert(static_cast(sqrt_un1) == UINT64_C(589847733628674383), "Error in constexpr integer square root"); + static_assert(static_cast(sqrt_un2) == UINT64_C(444334595153395054), "Error in constexpr integer square root"); + static_assert(static_cast(sqrt_un3) == UINT64_C(334718981177642730), "Error in constexpr integer square root"); + + static_assert(sqrt(boost::multiprecision::uint128_t(0)) == 0, "Error in constexpr integer square root"); + static_assert(sqrt(boost::multiprecision::uint128_t(1)) == 1, "Error in constexpr integer square root"); + static_assert(sqrt(boost::multiprecision::uint128_t(2)) == 1, "Error in constexpr integer square root"); + + // N[Sqrt[2^128 - 1], 50] + // 1.8446744073709551615999999999999999999972894945688*10^19 + static_assert(sqrt((std::numeric_limits::max)()) == boost::multiprecision::uint128_t(UINT64_C(18446744073709551615)), "Error in constexpr integer square root"); + + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("613111418242260482895474318171556018")) == boost::multiprecision::uint128_t(UINT64_C(783014315477220686))); + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("347920348866883608793309754527486715")) == boost::multiprecision::uint128_t(UINT64_C(589847733628674383))); + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("197433232450131483625758030703105499")) == boost::multiprecision::uint128_t(UINT64_C(444334595153395054))); + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("112036796360599148200087404494586705")) == boost::multiprecision::uint128_t(UINT64_C(334718981177642730))); + + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("0")) == boost::multiprecision::uint128_t(UINT8_C(0))); + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("1")) == boost::multiprecision::uint128_t(UINT8_C(1))); + BOOST_TEST(sqrt(boost::multiprecision::uint128_t("2")) == boost::multiprecision::uint128_t(UINT8_C(1))); + } + } } // namespace local auto main() -> int { + local::test_sqrt_integral_and_constexpr(); + #if defined(TEST_CPP_DOUBLE_FLOAT) { using float_type = boost::multiprecision::cpp_double_float; @@ -1535,6 +1691,7 @@ auto main() -> int static_cast(local::test_edges()); static_cast(local::test_edges_extra()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1549,6 +1706,7 @@ auto main() -> int static_cast(local::test_edges()); static_cast(local::test_edges_extra()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1563,6 +1721,7 @@ auto main() -> int static_cast(local::test_edges()); static_cast(local::test_edges_extra()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1580,6 +1739,7 @@ auto main() -> int std::cout << "Testing type: " << typeid(float_type).name() << std::endl; static_cast(local::test_edges()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1595,6 +1755,7 @@ auto main() -> int std::cout << "Testing type: " << typeid(float_type).name() << std::endl; static_cast(local::test_edges()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1610,6 +1771,7 @@ auto main() -> int std::cout << "Testing type: " << typeid(float_type).name() << std::endl; static_cast(local::test_edges()); + local::test_fdim_edge(); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -1626,8 +1788,6 @@ template auto my_inf () noexcept -> FloatType& { using float template auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } template auto my_exp1() noexcept -> FloatType& { using float_type = FloatType; static float_type val_exp1 { "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082265" }; return val_exp1; } -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) +#if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic pop #endif diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index a0b6257e3..e2a0191d2 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -5,8 +5,9 @@ // Some parts of this test file have been taken from the Boost.Decimal project. -#include #include +#include +#include #include // Note: include this AFTER the test-backends are defined @@ -17,10 +18,7 @@ #include #include -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wfloat-equal" -#elif defined(__GNUC__) +#if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" #endif @@ -74,13 +72,79 @@ namespace local { delta = fabs(a - b); // LCOV_EXCL_LINE - result_is_ok = (delta < tol); // LCOV_EXCL_LINE + result_is_ok = (delta <= tol); // LCOV_EXCL_LINE } else { delta = fabs(1 - (a / b)); - result_is_ok = (delta < tol); + result_is_ok = (delta <= tol); + } + + return result_is_ok; + } + + template + bool test_convert_and_back(const float epsilon_factor) + { + using any_float_type = AnyFloatType; + using other_float_type = OtherFloatType; + + std::mt19937_64 gen { time_point() }; + + auto dis = + std::uniform_real_distribution + { + static_cast(-1.5F), + static_cast(1.5F) + }; + + bool result_is_ok { true }; + + for(int n_loop = static_cast(INT8_C(0)); n_loop < static_cast(INT8_C(64)); ++n_loop) + { + static_cast(n_loop); + + using string_data_array_type = std::array; + + // Table[N[Exp[Pi EulerGamma*m], 100], {m, 1, 41, 10}] + // 6.131114182422604828954743181715560166328314533478636289880930665602805209787080979043183175178343525 + // 4.601860472890328982970020344164597095017995523702194983334509372565826509463730150117899613177592949*10^8 + // 3.454041008184686240269652058630805927805404685705614002929256804267342980143530922708514273650048389*10^16 + // 2.592516517287682590319488890844344913780990651072642232646840608443538857001634005575916140667342752*10^24 + // 1.945877850460679347837790546862626024227287564971475316486097406542338497232351222296834286913627889*10^32 + + const string_data_array_type + float_number_strings + { + std::string("6.131114182422604828954743181715560166328314533478636289880930665602805209787080979043183175178343525"), + std::string("4.601860472890328982970020344164597095017995523702194983334509372565826509463730150117899613177592949E8"), + std::string("3.454041008184686240269652058630805927805404685705614002929256804267342980143530922708514273650048389E16"), + std::string("2.592516517287682590319488890844344913780990651072642232646840608443538857001634005575916140667342752E24"), + std::string("1.945877850460679347837790546862626024227287564971475316486097406542338497232351222296834286913627889E32") + }; + + for(std::size_t index { std::size_t { UINT8_C(0) }}; index < std::tuple_size::value; ++index) + { + const any_float_type start(any_float_type(float_number_strings[index]) * (dis(gen)* dis(gen))); + const other_float_type other(start); + const any_float_type backto(other); + + const bool + result_of_trip_is_ok + { + local::is_close_fraction + ( + start, + backto, + any_float_type(std::numeric_limits::epsilon() * epsilon_factor) + ) + }; + + BOOST_TEST(result_of_trip_is_ok); + + result_is_ok = (result_of_trip_is_ok && result_is_ok); + } } return result_is_ok; @@ -194,12 +258,15 @@ namespace local const std::string str_ctrl { str128_maker(flt_n128) }; const std::string str_n128 { str128_maker(static_cast(val_n128)) }; + // This test assumes that boost::int128_type is as wide or wider than signed long long. + // Consider using a a kind of if-constexpr to verify this "up front". + result_val_n128_is_ok = ( (str_n128 == str_ctrl) && ( - (!is_neg) ? (val_n128 > static_cast((std::numeric_limits::max)())) - : (val_n128 < static_cast((std::numeric_limits::max)())) + (!is_neg) ? (val_n128 >= static_cast((std::numeric_limits::max)())) + : (val_n128 <= static_cast((std::numeric_limits::max)())) ) ); @@ -291,6 +358,106 @@ namespace local result_is_ok = (result_funky_strings_is_ok && result_is_ok); } + { + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) + { + static_cast(index); + + float_type flt_nrm { ::my_one() * dis(gen) }; + + const bool is_neg { ((index & 1U) != 0U) }; + + if(is_neg) + { + flt_nrm = -flt_nrm; + } + + const float_type flt_zer { ::my_zero() * dis(gen) }; + + flt_nrm /= flt_zer; + + bool result_div_zero_is_ok { (boost::multiprecision::isinf)(flt_nrm) }; + + if(is_neg) + { + result_div_zero_is_ok = ((boost::multiprecision::signbit)(flt_nrm) && result_div_zero_is_ok); + } + + BOOST_TEST(result_div_zero_is_ok); + + result_is_ok = (result_div_zero_is_ok && result_is_ok); + } + } + + { + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) + { + static_cast(index); + + float_type flt_nrm { ::my_one() + 0.25F }; + float_type flt_one { ::my_one() + ::my_zero() * dis(gen) }; + + const bool is_neg { ((index & 1U) != 0U) }; + + if(is_neg) + { + flt_one = -flt_one; + } + + flt_nrm /= flt_one; + + bool result_div_one_is_ok { (is_neg ? (flt_nrm == -1.25F) : (flt_nrm == 1.25F)) }; + + BOOST_TEST(result_div_one_is_ok); + + result_is_ok = (result_div_one_is_ok && result_is_ok); + } + } + + constexpr unsigned max_index { 1024U }; + + { + using std::ldexp; + + float_type flt_near_min { ldexp((std::numeric_limits::min)(), +64) }; + + unsigned index { }; + + while((index < max_index) && (!((boost::multiprecision::fpclassify)(flt_near_min) == FP_ZERO))) + { + flt_near_min /= static_cast(0xDEADBEEF); + + ++index; + } + + const bool result_unf_is_ok { ((index > 1U) && (index < max_index)) }; + + BOOST_TEST(result_unf_is_ok); + + result_is_ok = (result_unf_is_ok && result_is_ok); + } + + { + using std::ldexp; + + float_type flt_near_min { ldexp((std::numeric_limits::min)(), +64) }; + + unsigned index { }; + + while((index < max_index) && (!((boost::multiprecision::fpclassify)(flt_near_min) == FP_ZERO))) + { + flt_near_min /= static_cast(100000000ULL - 3ULL); + + ++index; + } + + const bool result_unf_is_ok { ((index > 1U) && (index < max_index)) }; + + BOOST_TEST(result_unf_is_ok); + + result_is_ok = (result_unf_is_ok && result_is_ok); + } + { using std::ldexp; @@ -300,14 +467,14 @@ namespace local unsigned index { }; - while((index < 1024U) && (!(boost::multiprecision::isinf)(flt_near_max))) + while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_max))) { flt_near_max += (flt_less_near_max * dis(gen)); ++index; } - const bool result_ovf_is_ok { ((index > 1U) && (index < 1024U)) }; + const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_ovf_is_ok); @@ -323,14 +490,14 @@ namespace local unsigned index { }; - while((index < 1024U) && (!(boost::multiprecision::isinf)(flt_near_lowest))) + while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_lowest))) { flt_near_lowest -= (flt_less_near_max * dis(gen)); ++index; } - const bool result_ovf_is_ok { ((index > 1U) && (index < 1024U)) && signbit(flt_near_lowest) }; + const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) && signbit(flt_near_lowest) }; BOOST_TEST(result_ovf_is_ok); @@ -346,14 +513,14 @@ namespace local unsigned index { }; - while((index < 1024U) && (!(boost::multiprecision::isinf)(flt_near_max))) + while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_max))) { flt_near_max *= static_cast(INT32_C(2)); ++index; } - const bool result_ovf_is_ok { ((index > 1U) && (index < 1024U)) }; + const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_ovf_is_ok); @@ -387,21 +554,34 @@ namespace local { static_cast(index); - float_type flt_zero_numerator { ::my_zero() * dis(gen) }; - float_type flt_finite_numerator { ::my_one() * dis(gen) }; + float_type flt_zer_numerator { ::my_zero() * dis(gen) }; + float_type flt_nrm_numerator { ::my_one() * dis(gen) }; float_type flt_nan_numerator { ::my_nan() * dis(gen) }; + float_type flt_inf_numerator { ::my_inf() * dis(gen) }; - const float_type flt_infinite_result_neg { -flt_finite_numerator /= static_cast(INT8_C(0)) }; + const float_type flt_inf_result_neg_01 { -flt_nrm_numerator /= static_cast(INT8_C(0)) }; + const float_type flt_inf_result_neg_02 { -flt_inf_numerator *= static_cast(index + 2U) }; - const bool result_edge_00 { (boost::multiprecision::isnan)(flt_zero_numerator /= static_cast(INT8_C(0))) }; - const bool result_edge_01 { (boost::multiprecision::isinf)(flt_finite_numerator /= static_cast(INT8_C(0))) }; - const bool result_edge_02 { (boost::multiprecision::isinf)(flt_infinite_result_neg) && (boost::multiprecision::signbit)(flt_infinite_result_neg) }; + const bool result_edge_00 { (boost::multiprecision::isnan)(flt_zer_numerator /= static_cast(INT8_C(0))) }; + const bool result_edge_01 { (boost::multiprecision::isinf)(flt_nrm_numerator /= static_cast(INT8_C(0))) }; + const bool result_edge_02 { (boost::multiprecision::isinf)(flt_inf_result_neg_01) && (boost::multiprecision::signbit)(flt_inf_result_neg_01) }; const bool result_edge_03 { (boost::multiprecision::isnan)(flt_nan_numerator /= static_cast(index + 2U)) }; + const bool result_edge_04 { (boost::multiprecision::isnan)(flt_nan_numerator *= static_cast(index + 2U)) }; + const bool result_edge_05 { (boost::multiprecision::isnan)(flt_inf_numerator *= static_cast(0)) }; + const bool result_edge_06 { (boost::multiprecision::isinf)(flt_inf_result_neg_02) && (boost::multiprecision::signbit)(flt_inf_result_neg_02) }; const bool result_divs_are_ok { - (result_edge_00 && result_edge_01 && result_edge_02 && result_edge_03) + ( + result_edge_00 + && result_edge_01 + && result_edge_02 + && result_edge_03 + && result_edge_04 + && result_edge_05 + && result_edge_06 + ) }; BOOST_TEST(result_divs_are_ok); @@ -413,6 +593,23 @@ namespace local return result_is_ok; } + template + auto test_convert_and_back_caller(const float epsilon_factor = 0.0F) -> bool + { + using other_float_type = OtherFloatType; + + std::cout << "Testing type (in test_convert_and_back): " << typeid(other_float_type).name() << std::endl; + + const bool + result_of_trip_is_ok + { + local::test_convert_and_back(epsilon_factor) + }; + + BOOST_TEST(result_of_trip_is_ok); + + return result_of_trip_is_ok; + } } // namespace local auto main() -> int @@ -437,6 +634,17 @@ auto main() -> int static_cast(local::test_edges()); } + { + using big_bin_float_backend_type = boost::multiprecision::cpp_bin_float<512>; + using big_dec_float_backend_type = boost::multiprecision::cpp_dec_float<512>; + + using big_bin_float_type = boost::multiprecision::number; + using big_dec_float_type = boost::multiprecision::number; + + static_cast(local::test_convert_and_back_caller(0.25F)); + static_cast(local::test_convert_and_back_caller(0.50F)); + } + return boost::report_errors(); } @@ -445,8 +653,6 @@ template auto my_one () noexcept -> FloatType& { using float template auto my_inf () noexcept -> FloatType& { using float_type = FloatType; static float_type val_inf { std::numeric_limits::infinity() }; return val_inf; } template auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) +#if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic pop #endif From 5b7c6767bf91c8d2baba6c36dcc73c7b7c2f5524 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Thu, 3 Jul 2025 19:09:10 +0200 Subject: [PATCH 002/114] Repair epsilon but still open for evolution --- .../boost/multiprecision/cpp_dec_float.hpp | 29 +++++++++++++++++-- .../boost/multiprecision/cpp_double_fp.hpp | 3 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index df1a173bd..57584c0ea 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -446,6 +446,7 @@ class cpp_dec_float } bool isone() const; + bool isone_minus() const; bool isint() const; bool isneg() const { return neg; } @@ -1228,9 +1229,9 @@ cpp_dec_float& cpp_dec_float::isone() const return false; } +template +bool cpp_dec_float::isone_minus() const +{ + // Check if the value of *this is identically 1 or very close to 1. + + const bool is_negative_and_is_finite = (neg && (isfinite)()); + + if (is_negative_and_is_finite) + { + if ((data[0u] == static_cast(1u)) && (exp == static_cast(0))) + { + const typename array_type::const_iterator it_non_zero = std::find_if(data.begin(), data.end(), data_elem_is_non_zero_predicate); + return (it_non_zero == data.end()); + } + else if ((data[0u] == static_cast(cpp_dec_float_elem_mask - 1)) && (exp == static_cast(-cpp_dec_float_elem_digits10))) + { + const typename array_type::const_iterator it_non_nine = std::find_if(data.begin(), data.end(), data_elem_is_non_nine_predicate); + return (it_non_nine == data.end()); + } + } + + return false; +} + template bool cpp_dec_float::isint() const { diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 9049e0bcc..22b79b671 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -991,8 +991,7 @@ class cpp_double_fp_backend constexpr cpp_double_fp_backend my_value_eps_constexpr { - cpp_double_fp_backend(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) - * cpp_double_fp_backend(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) + cpp_df_qf_detail::ccmath::unsafe::ldexp(float_type { 1 }, int { 3 - my_digits }) }; static_assert From b196bcccd384aa2855bfae63f49ec8aae0c0be82 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Thu, 3 Jul 2025 21:14:41 +0200 Subject: [PATCH 003/114] Rework some functions --- .../boost/multiprecision/cpp_dec_float.hpp | 66 ++++++++----------- 1 file changed, 29 insertions(+), 37 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 57584c0ea..9d8a30eac 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -445,8 +445,6 @@ class cpp_dec_float return ((fpclass == cpp_dec_float_finite) && (data[0u] == 0u)); } - bool isone() const; - bool isone_minus() const; bool isint() const; bool isneg() const { return neg; } @@ -571,6 +569,17 @@ class cpp_dec_float // Inversion. cpp_dec_float& calculate_inv(); + bool isone() const + { + bool b_neg; + + const bool result_one_is_ok = is_one_or_minus_one(&b_neg); + + return (result_one_is_ok && (!b_neg)); + } + + bool is_one_or_minus_one(bool* p_sign) const; + void from_unsigned_long_long(const unsigned long long u); template & cpp_dec_float& cpp_dec_float::compare(const cpp_dec_floa if (iszero()) { // The value of *this is zero and v is either zero or non-zero. - return (v.iszero() ? 0 - : (v.neg ? 1 : -1)); + return (v.iszero() ? 0 : (v.neg ? 1 : -1)); } else if (v.iszero()) { @@ -1456,47 +1465,30 @@ int cpp_dec_float::compare(const cpp_dec_floa } template -bool cpp_dec_float::isone() const +bool cpp_dec_float::is_one_or_minus_one(bool* p_sign) const { // Check if the value of *this is identically 1 or very close to 1. - const bool not_negative_and_is_finite = ((!neg) && (isfinite)()); + if(p_sign != nullptr) + *p_sign = neg; - if (not_negative_and_is_finite) + if ((isfinite)()) { if ((data[0u] == static_cast(1u)) && (exp == static_cast(0))) { const typename array_type::const_iterator it_non_zero = std::find_if(data.begin(), data.end(), data_elem_is_non_zero_predicate); - return (it_non_zero == data.end()); - } - else if ((data[0u] == static_cast(cpp_dec_float_elem_mask - 1)) && (exp == static_cast(-cpp_dec_float_elem_digits10))) - { - const typename array_type::const_iterator it_non_nine = std::find_if(data.begin(), data.end(), data_elem_is_non_nine_predicate); - return (it_non_nine == data.end()); - } - } - - return false; -} - -template -bool cpp_dec_float::isone_minus() const -{ - // Check if the value of *this is identically 1 or very close to 1. - const bool is_negative_and_is_finite = (neg && (isfinite)()); + const bool result_one_is_ok = (it_non_zero == data.end()); - if (is_negative_and_is_finite) - { - if ((data[0u] == static_cast(1u)) && (exp == static_cast(0))) - { - const typename array_type::const_iterator it_non_zero = std::find_if(data.begin(), data.end(), data_elem_is_non_zero_predicate); - return (it_non_zero == data.end()); + return result_one_is_ok; } else if ((data[0u] == static_cast(cpp_dec_float_elem_mask - 1)) && (exp == static_cast(-cpp_dec_float_elem_digits10))) { const typename array_type::const_iterator it_non_nine = std::find_if(data.begin(), data.end(), data_elem_is_non_nine_predicate); - return (it_non_nine == data.end()); + + const bool result_one_is_ok = (it_non_nine == data.end()); + + return result_one_is_ok; } } From da2f57fc68a8cda19653049d88e27305615c6a68 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Fri, 4 Jul 2025 06:30:53 +0200 Subject: [PATCH 004/114] Make better use of compiler switches --- include/boost/multiprecision/detail/default_ops.hpp | 10 ++++------ test/test_various_edges.cpp | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index 6e1aeb354..0e8debe1e 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -1803,9 +1803,7 @@ BOOST_MP_CXX14_CONSTEXPR void eval_karatsuba_sqrt(Backend& result, const Backend result = s; } -// LCOV_EXCL_START -// This is known tested in the test-file test_various_edges.cpp. - +#ifndef BOOST_MP_NO_CONSTEXPR_DETECTION template void BOOST_MP_CXX14_CONSTEXPR eval_integer_sqrt_bitwise(B& s, B& r, const B& x) { @@ -1863,8 +1861,7 @@ void BOOST_MP_CXX14_CONSTEXPR eval_integer_sqrt_bitwise(B& s, B& r, const B& x) --g; } while (g >= 0); } - -// LCOV_EXCL_STOP +#endif // !BOOST_MP_NO_CONSTEXPR_DETECTION template BOOST_MP_CXX14_CONSTEXPR void eval_integer_sqrt(Backend& result, Backend& r, const Backend& x) @@ -1873,7 +1870,8 @@ BOOST_MP_CXX14_CONSTEXPR void eval_integer_sqrt(Backend& result, Backend& r, con // recursive Karatsuba sqrt can cause issues in constexpr context: if (BOOST_MP_IS_CONST_EVALUATED(result.size())) return eval_integer_sqrt_bitwise(result, r, x); -#endif +#endif // !BOOST_MP_NO_CONSTEXPR_DETECTION + using small_uint = typename std::tuple_element<0, typename Backend::unsigned_types>::type; constexpr small_uint zero = 0u; diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 03363efc7..260b5c624 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -1620,6 +1620,7 @@ namespace local auto test_sqrt_integral_and_constexpr() -> void { + #ifndef BOOST_MP_NO_CONSTEXPR_DETECTION { // Select some pseudo-random integers @@ -1676,6 +1677,7 @@ namespace local BOOST_TEST(sqrt(boost::multiprecision::uint128_t("1")) == boost::multiprecision::uint128_t(UINT8_C(1))); BOOST_TEST(sqrt(boost::multiprecision::uint128_t("2")) == boost::multiprecision::uint128_t(UINT8_C(1))); } + #endif // !BOOST_MP_NO_CONSTEXPR_DETECTION } } // namespace local From 590d9565a319e49eb80ef31cc4f64ad63a2c08bd Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Fri, 4 Jul 2025 11:36:24 +0200 Subject: [PATCH 005/114] Rework shadow vars and string over/underflow --- .../boost/multiprecision/cpp_dec_float.hpp | 240 +++++++++--------- test/test_various_edges_more.cpp | 100 ++++++++ 2 files changed, 224 insertions(+), 116 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 9d8a30eac..b2604dd45 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -553,11 +553,12 @@ class cpp_dec_float void serialize(Archive& ar, const unsigned int /*version*/) { for (unsigned i = 0; i < data.size(); ++i) - ar& boost::make_nvp("digit", data[i]); - ar& boost::make_nvp("exponent", exp); - ar& boost::make_nvp("sign", neg); - ar& boost::make_nvp("class-type", fpclass); - ar& boost::make_nvp("precision", prec_elem); + ar & boost::make_nvp("digit", data[i]); + + ar & boost::make_nvp("exponent", exp); + ar & boost::make_nvp("sign", neg); + ar & boost::make_nvp("class-type", fpclass); + ar & boost::make_nvp("precision", prec_elem); } #endif @@ -1612,13 +1613,13 @@ double cpp_dec_float::extract_double() const : -std::numeric_limits::infinity()); } - std::stringstream ss; - ss.imbue(std::locale::classic()); + std::stringstream strm; + strm.imbue(std::locale::classic()); - ss << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); + strm << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); double d; - ss >> d; + strm >> d; return d; } @@ -1659,13 +1660,13 @@ long double cpp_dec_float::extract_long_doubl : -std::numeric_limits::infinity()); } - std::stringstream ss; - ss.imbue(std::locale::classic()); + std::stringstream strm; + strm.imbue(std::locale::classic()); - ss << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); + strm << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); long double ld; - ss >> ld; + strm >> ld; return ld; } @@ -1936,9 +1937,9 @@ std::string cpp_dec_float::str(std::intmax_t return "nan"; } - std::string str; + std::string my_str; std::intmax_t org_digits(number_of_digits); - exponent_type my_exp = order(); + exponent_type my_exp = order(); if (!(f & std::ios_base::fixed) && (number_of_digits == 0)) number_of_digits = cpp_dec_float_max_digits10; @@ -1954,17 +1955,17 @@ std::string cpp_dec_float::str(std::intmax_t static_cast(cpp_dec_float_elem_number)); // Extract the remaining digits from cpp_dec_float after the decimal point. - std::stringstream ss; - ss.imbue(std::locale::classic()); - ss << data[0]; + std::stringstream strm; + strm.imbue(std::locale::classic()); + strm << data[0]; // Extract all of the digits from cpp_dec_float, beginning with the first data element. for (std::size_t i = static_cast(1u); i < number_of_elements; i++) { - ss << std::setw(static_cast(cpp_dec_float_elem_digits10)) - << std::setfill(static_cast('0')) - << data[i]; + strm << std::setw(static_cast(cpp_dec_float_elem_digits10)) + << std::setfill(static_cast('0')) + << data[i]; } - str += ss.str(); + my_str += strm.str(); bool have_leading_zeros = false; @@ -1975,37 +1976,37 @@ std::string cpp_dec_float::str(std::intmax_t number_of_digits -= my_exp + 1; // reset to original value if (number_of_digits) { - str.insert(static_cast(0), std::string::size_type(number_of_digits), '0'); + my_str.insert(static_cast(0), std::string::size_type(number_of_digits), '0'); have_leading_zeros = true; } } if (number_of_digits < 0) { - str = "0"; + my_str = "0"; if (isneg()) - str.insert(static_cast(0), 1, '-'); - boost::multiprecision::detail::format_float_string(str, 0, number_of_digits - my_exp - 1, f, this->iszero()); - return str; + my_str.insert(static_cast(0), 1, '-'); + boost::multiprecision::detail::format_float_string(my_str, 0, number_of_digits - my_exp - 1, f, this->iszero()); + return my_str; } else { // Cut the output to the size of the precision. - if (str.length() > static_cast(number_of_digits)) + if (my_str.length() > static_cast(number_of_digits)) { // Get the digit after the last needed digit for rounding - const std::uint32_t round = static_cast(static_cast(str[static_cast(number_of_digits)]) - static_cast('0')); + const std::uint32_t round = static_cast(static_cast(my_str[static_cast(number_of_digits)]) - static_cast('0')); bool need_round_up = round >= 5u; if (round == 5u) { - const std::uint32_t ix = number_of_digits == 0 ? 0 : static_cast(static_cast(str[static_cast(number_of_digits - 1)]) - static_cast('0')); + const std::uint32_t ix = number_of_digits == 0 ? 0 : static_cast(static_cast(my_str[static_cast(number_of_digits - 1)]) - static_cast('0')); if ((ix & 1u) == 0) { // We have an even digit followed by a 5, so we might not actually need to round up // if all the remaining digits are zero: - if (str.find_first_not_of('0', static_cast(number_of_digits + 1)) == std::string::npos) + if (my_str.find_first_not_of('0', static_cast(number_of_digits + 1)) == std::string::npos) { bool all_zeros = true; // No none-zero trailing digits in the string, now check whatever parts we didn't convert to the string: @@ -2024,45 +2025,45 @@ std::string cpp_dec_float::str(std::intmax_t } // Truncate the string - str.erase(static_cast(number_of_digits)); + my_str.erase(static_cast(number_of_digits)); if (need_round_up) { - if (str.size()) + if (my_str.size()) { - std::size_t ix = static_cast(str.length() - 1u); + std::size_t ix = static_cast(my_str.length() - 1u); // Every trailing 9 must be rounded up - while (ix && (static_cast(str.at(ix)) - static_cast('0') == static_cast(9))) + while (ix && (static_cast(my_str.at(ix)) - static_cast('0') == static_cast(9))) { - str.at(ix) = static_cast('0'); + my_str.at(ix) = static_cast('0'); --ix; } if (!ix) { // There were nothing but trailing nines. - if (static_cast(static_cast(str.at(ix)) - static_cast(0x30)) == static_cast(9)) + if (static_cast(static_cast(my_str.at(ix)) - static_cast(0x30)) == static_cast(9)) { // Increment up to the next order and adjust exponent. - str.at(ix) = static_cast('1'); + my_str.at(ix) = static_cast('1'); ++my_exp; } else { // Round up this digit. - ++str.at(ix); + ++my_str.at(ix); } } else { // Round up the last digit. - ++str[ix]; + ++my_str[ix]; } } else { - str = "1"; + my_str = "1"; ++my_exp; } } @@ -2073,20 +2074,20 @@ std::string cpp_dec_float::str(std::intmax_t { // We need to take the zeros back out again, and correct the exponent // if we rounded up: - if (str[std::string::size_type(number_of_digits - 1)] != '0') + if (my_str[std::string::size_type(number_of_digits - 1)] != '0') { ++my_exp; - str.erase(0, std::string::size_type(number_of_digits - 1)); + my_str.erase(0, std::string::size_type(number_of_digits - 1)); } else - str.erase(0, std::string::size_type(number_of_digits)); + my_str.erase(0, std::string::size_type(number_of_digits)); } if (isneg()) - str.insert(static_cast(0), 1, '-'); + my_str.insert(static_cast(0), 1, '-'); - boost::multiprecision::detail::format_float_string(str, my_exp, org_digits, f, this->iszero()); - return str; + boost::multiprecision::detail::format_float_string(my_str, my_exp, org_digits, f, this->iszero()); + return my_str; } template @@ -2097,7 +2098,7 @@ bool cpp_dec_float::rd_string(const char* con { #endif - std::string str(s); + std::string my_str(s); static const std::string valid_characters{"0123456789"}; // TBD: Using several regular expressions may significantly reduce @@ -2108,39 +2109,39 @@ bool cpp_dec_float::rd_string(const char* con std::size_t pos; - if (((pos = str.find('e')) != std::string::npos) || ((pos = str.find('E')) != std::string::npos)) + if (((pos = my_str.find('e')) != std::string::npos) || ((pos = my_str.find('E')) != std::string::npos)) { // Remove the exponent part from the string. #ifndef BOOST_MP_STANDALONE - exp = boost::lexical_cast(static_cast(str.c_str() + (pos + 1u))); + exp = boost::lexical_cast(static_cast(my_str.c_str() + (pos + 1u))); #else - if (str.find_first_not_of(valid_characters, ((str[pos + 1] == '+') || (str[pos + 1] == '-')) ? pos + 2 : pos + 1) != std::string::npos) + if (my_str.find_first_not_of(valid_characters, ((my_str[pos + 1] == '+') || (my_str[pos + 1] == '-')) ? pos + 2 : pos + 1) != std::string::npos) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); - exp = static_cast(std::atoll(static_cast(str.c_str() + (pos + 1u)))); + exp = static_cast(std::atoll(static_cast(my_str.c_str() + (pos + 1u)))); #endif - - str = str.substr(static_cast(0u), pos); + + my_str = my_str.substr(static_cast(0u), pos); } // Get a possible +/- sign and remove it. neg = false; - if (str.size()) + if (my_str.size()) { - if (str[0] == '-') + if (my_str[0] == '-') { neg = true; - str.erase(0, 1); + my_str.erase(0, 1); } - else if (str[0] == '+') + else if (my_str[0] == '+') { - str.erase(0, 1); + my_str.erase(0, 1); } } // // Special cases for infinities and NaN's: // - if ((str == "inf") || (str == "INF") || (str == "infinity") || (str == "INFINITY")) + if ((my_str == "inf") || (my_str == "INF") || (my_str == "infinity") || (my_str == "INFINITY")) { if (neg) { @@ -2151,18 +2152,18 @@ bool cpp_dec_float::rd_string(const char* con *this = this->inf(); return true; } - if ((str.size() >= 3) && ((str.substr(0, 3) == "nan") || (str.substr(0, 3) == "NAN") || (str.substr(0, 3) == "NaN"))) + if ((my_str.size() >= 3) && ((my_str.substr(0, 3) == "nan") || (my_str.substr(0, 3) == "NAN") || (my_str.substr(0, 3) == "NaN"))) { *this = this->nan(); return true; } // Remove the leading zeros for all input types. - const std::string::iterator fwd_it_leading_zero = std::find_if(str.begin(), str.end(), char_is_nonzero_predicate); + const std::string::iterator fwd_it_leading_zero = std::find_if(my_str.begin(), my_str.end(), char_is_nonzero_predicate); - if (fwd_it_leading_zero != str.begin()) + if (fwd_it_leading_zero != my_str.begin()) { - if (fwd_it_leading_zero == str.end()) + if (fwd_it_leading_zero == my_str.end()) { // The string contains nothing but leading zeros. // This string represents zero. @@ -2171,7 +2172,7 @@ bool cpp_dec_float::rd_string(const char* con } else { - str.erase(str.begin(), fwd_it_leading_zero); + my_str.erase(my_str.begin(), fwd_it_leading_zero); } } @@ -2182,32 +2183,33 @@ bool cpp_dec_float::rd_string(const char* con // even multiple of cpp_dec_float_elem_digits10. // Find a possible decimal point. - pos = str.find(static_cast('.')); + pos = my_str.find(static_cast('.')); if (pos != std::string::npos) { // Check we have only digits either side of the point: - if (str.find_first_not_of(valid_characters) != pos) + if (my_str.find_first_not_of(valid_characters) != pos) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); - if (str.find_first_not_of(valid_characters, pos + 1) != std::string::npos) + if (my_str.find_first_not_of(valid_characters, pos + 1) != std::string::npos) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // Remove all trailing insignificant zeros. - const std::string::const_reverse_iterator rit_non_zero = std::find_if(str.rbegin(), str.rend(), char_is_nonzero_predicate); + const std::string::const_reverse_iterator rit_non_zero = std::find_if(my_str.rbegin(), my_str.rend(), char_is_nonzero_predicate); - if (rit_non_zero != static_cast(str.rbegin())) + if (rit_non_zero != static_cast(my_str.rbegin())) { const std::string::size_type ofs = static_cast ( - static_cast(str.length()) - - std::distance(str.rbegin(), rit_non_zero) + static_cast(my_str.length()) + - std::distance(my_str.rbegin(), rit_non_zero) ); - str.erase(str.begin() + static_cast(ofs), str.end()); + + my_str.erase(my_str.begin() + static_cast(ofs), my_str.end()); } // Check if the input is identically zero. - if (str == std::string(".")) + if (my_str == std::string(".")) { operator=(zero()); return true; @@ -2217,31 +2219,31 @@ bool cpp_dec_float::rd_string(const char* con // and adjust the exponent accordingly. // Note that the while-loop operates only on strings of the form ".000abcd..." // and peels away the zeros just after the decimal point. - if (str.at(static_cast(0u)) == static_cast('.')) + if (my_str.at(static_cast(0u)) == static_cast('.')) { - const std::string::iterator it_non_zero = std::find_if(str.begin() + 1u, str.end(), char_is_nonzero_predicate); + const std::string::iterator it_non_zero = std::find_if(my_str.begin() + 1u, my_str.end(), char_is_nonzero_predicate); std::size_t delta_exp = static_cast(0u); - if (str.at(static_cast(1u)) == static_cast('0')) + if (my_str.at(static_cast(1u)) == static_cast('0')) { - delta_exp = static_cast(std::distance(str.begin() + 1u, it_non_zero)); + delta_exp = static_cast(std::distance(my_str.begin() + 1u, it_non_zero)); } // Bring one single digit into the mantissa and adjust the exponent accordingly. - str.erase(str.begin(), it_non_zero); - str.insert(static_cast(1u), "."); + my_str.erase(my_str.begin(), it_non_zero); + my_str.insert(static_cast(1u), "."); exp -= static_cast(delta_exp + 1u); } } else { // We should have only digits: - if (str.find_first_not_of(valid_characters) != std::string::npos) + if (my_str.find_first_not_of(valid_characters) != std::string::npos) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // Input string has no decimal point: Append decimal point. - str.append("."); + my_str.append("."); } // Shift the decimal point such that the exponent is an even multiple of cpp_dec_float_elem_digits10. @@ -2256,29 +2258,29 @@ bool cpp_dec_float::rd_string(const char* con } // Make sure that there are enough digits for the decimal point shift. - pos = str.find(static_cast('.')); + pos = my_str.find(static_cast('.')); std::ptrdiff_t pos_plus_one = static_cast(pos + 1); - if ((static_cast(str.length()) - pos_plus_one) < n_shift) + if ((static_cast(my_str.length()) - pos_plus_one) < n_shift) { - const std::ptrdiff_t sz = static_cast(n_shift - (static_cast(str.length()) - pos_plus_one)); + const std::ptrdiff_t sz = static_cast(n_shift - (static_cast(my_str.length()) - pos_plus_one)); - str.append(std::string(static_cast(sz), static_cast('0'))); + my_str.append(std::string(static_cast(sz), static_cast('0'))); } // Do the decimal point shift. if (n_shift != static_cast(0)) { - str.insert(static_cast(pos_plus_one + n_shift), "."); + my_str.insert(static_cast(pos_plus_one + n_shift), "."); - str.erase(pos, static_cast(1)); + my_str.erase(pos, static_cast(1)); exp -= static_cast(n_shift); } // Cut the size of the mantissa to <= cpp_dec_float_elem_digits10. - pos = str.find(static_cast('.')); + pos = my_str.find(static_cast('.')); pos_plus_one = static_cast(pos + 1u); if (pos > static_cast(cpp_dec_float_elem_digits10)) @@ -2287,25 +2289,25 @@ bool cpp_dec_float::rd_string(const char* con const std::int32_t n_rem_is_zero = ((static_cast(n_pos % cpp_dec_float_elem_digits10) == static_cast(0)) ? static_cast(1) : static_cast(0)); const std::int32_t n = static_cast(static_cast(n_pos / cpp_dec_float_elem_digits10) - n_rem_is_zero); - str.insert(static_cast(static_cast(n_pos - static_cast(n * cpp_dec_float_elem_digits10))), "."); + my_str.insert(static_cast(static_cast(n_pos - static_cast(n * cpp_dec_float_elem_digits10))), "."); - str.erase(static_cast(pos_plus_one), static_cast(1u)); + my_str.erase(static_cast(pos_plus_one), static_cast(1u)); exp += static_cast(static_cast(n) * static_cast(cpp_dec_float_elem_digits10)); } // Pad the decimal part such that its value is an even // multiple of cpp_dec_float_elem_digits10. - pos = str.find(static_cast('.')); + pos = my_str.find(static_cast('.')); pos_plus_one = static_cast(pos + 1u); // Throws an error for a strange construction like 3.14L - if(pos != std::string::npos && (str.back() == 'L' || str.back() == 'l' || str.back() == 'u' || str.back() == 'U')) + if(pos != std::string::npos && (my_str.back() == 'L' || my_str.back() == 'l' || my_str.back() == 'u' || my_str.back() == 'U')) { BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with an integer literal")); } - const std::int32_t n_dec = static_cast(static_cast(str.length() - 1u) - static_cast(pos)); + const std::int32_t n_dec = static_cast(static_cast(my_str.length() - 1u) - static_cast(pos)); const std::int32_t n_rem = static_cast(n_dec % cpp_dec_float_elem_digits10); std::int32_t n_cnt = ((n_rem != static_cast(0)) @@ -2314,16 +2316,16 @@ bool cpp_dec_float::rd_string(const char* con if (n_cnt != static_cast(0)) { - str.append(static_cast(n_cnt), static_cast('0')); + my_str.append(static_cast(n_cnt), static_cast('0')); } // Truncate decimal part if it is too long. const std::size_t max_dec = static_cast((cpp_dec_float_elem_number - 1) * cpp_dec_float_elem_digits10); - if (static_cast(str.length() - pos) > max_dec) + if (static_cast(my_str.length() - pos) > max_dec) { - str = str.substr(static_cast(0u), - static_cast(pos_plus_one + static_cast(max_dec))); + my_str = my_str.substr(static_cast(0u), + static_cast(pos_plus_one + static_cast(max_dec))); } // Now the input string has the standard cpp_dec_float input form. @@ -2335,19 +2337,19 @@ bool cpp_dec_float::rd_string(const char* con // Extract the data. // First get the digits to the left of the decimal point... - data[0u] = static_cast(std::stol(str.substr(static_cast(0u), pos))); + data[0u] = static_cast(std::stol(my_str.substr(static_cast(0u), pos))); // ...then get the remaining digits to the right of the decimal point. const std::string::size_type i_end = ( - static_cast(str.length() - static_cast(pos_plus_one)) + static_cast(my_str.length() - static_cast(pos_plus_one)) / static_cast(cpp_dec_float_elem_digits10) ); for (std::string::size_type i = static_cast(0u); i < i_end; i++) { const std::string::const_iterator it = - str.begin() + my_str.begin() + static_cast ( static_cast(pos_plus_one) @@ -2358,36 +2360,42 @@ bool cpp_dec_float::rd_string(const char* con } // Check for overflow... - if (exp > cpp_dec_float_max_exp10) + if (exp >= cpp_dec_float_max_exp10) { - const bool b_result_is_neg = neg; + const bool neg_tmp { neg }; - *this = inf(); - if (b_result_is_neg) - negate(); + neg = false; + + const int compare_result { compare((cpp_dec_float::max)()) }; + + if(compare_result > 0) + { + *this = inf(); + } + + neg = neg_tmp; } // ...and check for underflow. if (exp <= cpp_dec_float_min_exp10) { - if (exp == cpp_dec_float_min_exp10) - { - // Check for identity with the minimum value. - cpp_dec_float test = *this; + const bool neg_tmp { neg }; - test.exp = static_cast(0); + neg = false; - if (test.isone()) - { - *this = zero(); - } + const int compare_result { compare((cpp_dec_float::min)()) }; + + if(compare_result < 0) + { + *this = zero(); } else { - *this = zero(); + neg = neg_tmp; } } + #ifndef BOOST_NO_EXCEPTIONS } #ifndef BOOST_MP_STANDALONE diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index e2a0191d2..46bd34cf4 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -610,6 +610,105 @@ namespace local return result_of_trip_is_ok; } + + + template + auto test_cpp_dec_float_rd_ovf_unf() -> void + { + using local_cpp_dec_float_type = CppDecFloatType; + + { + typename local_cpp_dec_float_type::backend_type::exponent_type max_exp { }; + + std::string str_max_pos_01("+1.0E+"); + std::string str_max_pos_02("+2.0E+"); + std::string str_max_pos_03("+3.0E+"); + std::string str_max_pos_04("+0.9E+"); + + std::string str_max_neg_01("-1.0E+"); + std::string str_max_neg_02("-2.0E+"); + std::string str_max_neg_03("-3.0E+"); + std::string str_max_neg_04("-0.9E+"); + + { + std::stringstream strm { }; + + strm << std::numeric_limits::max_exponent10; + + str_max_pos_01 += strm.str(); + str_max_pos_02 += strm.str(); + str_max_pos_03 += strm.str(); + str_max_pos_04 += strm.str(); + + str_max_neg_01 += strm.str(); + str_max_neg_02 += strm.str(); + str_max_neg_03 += strm.str(); + str_max_neg_04 += strm.str(); + } + + const local_cpp_dec_float_type max_pos_01(str_max_pos_01); + const local_cpp_dec_float_type max_pos_02(str_max_pos_02); + const local_cpp_dec_float_type max_pos_03(str_max_pos_03); + const local_cpp_dec_float_type max_pos_04(str_max_pos_04); + + const local_cpp_dec_float_type max_neg_01(str_max_neg_01); + const local_cpp_dec_float_type max_neg_02(str_max_neg_02); + const local_cpp_dec_float_type max_neg_03(str_max_neg_03); + const local_cpp_dec_float_type max_neg_04(str_max_neg_04); + + using std::signbit; + + BOOST_TEST(max_pos_01 == (std::numeric_limits::max)()); + BOOST_TEST((boost::multiprecision::isinf)(max_pos_02) && (!signbit(max_pos_02))); + BOOST_TEST((boost::multiprecision::isinf)(max_pos_03) && (!signbit(max_pos_03))); + BOOST_TEST(((boost::multiprecision::fpclassify)(max_pos_04) == FP_NORMAL) && (!signbit(max_pos_04))); + + BOOST_TEST(max_neg_01 == (std::numeric_limits::lowest)()); + BOOST_TEST((boost::multiprecision::isinf)(max_neg_02) && signbit(max_neg_02)); + BOOST_TEST((boost::multiprecision::isinf)(max_neg_03) && signbit(max_neg_03)); + BOOST_TEST(((boost::multiprecision::fpclassify)(max_neg_04) == FP_NORMAL) && signbit(max_neg_04)); + } + + { + std::string str_min_pos_01("+1.0E"); + std::string str_min_pos_02("+1.1E"); + std::string str_min_pos_03("+0.9E"); + + std::string str_min_neg_01("-1.0E"); + std::string str_min_neg_02("-1.1E"); + std::string str_min_neg_03("-0.9E"); + + { + std::stringstream strm { }; + + strm << std::numeric_limits::min_exponent10; + + str_min_pos_01 += strm.str(); + str_min_pos_02 += strm.str(); + str_min_pos_03 += strm.str(); + + str_min_neg_01 += strm.str(); + str_min_neg_02 += strm.str(); + str_min_neg_03 += strm.str(); + } + + const local_cpp_dec_float_type min_pos_01(str_min_pos_01); + const local_cpp_dec_float_type min_pos_02(str_min_pos_02); + const local_cpp_dec_float_type min_pos_03(str_min_pos_03); + + const local_cpp_dec_float_type min_neg_01(str_min_neg_01); + const local_cpp_dec_float_type min_neg_02(str_min_neg_02); + const local_cpp_dec_float_type min_neg_03(str_min_neg_03); + + BOOST_TEST(min_pos_01 == (std::numeric_limits::min)()); + BOOST_TEST(((boost::multiprecision::fpclassify)(min_pos_02) == FP_NORMAL) && (!signbit(min_pos_02))); + BOOST_TEST(((boost::multiprecision::fpclassify)(min_pos_03) == FP_ZERO) && (!signbit(min_pos_03))); + + BOOST_TEST(min_neg_01 == -(std::numeric_limits::min)()); + BOOST_TEST(((boost::multiprecision::fpclassify)(min_neg_02) == FP_NORMAL) && signbit(min_neg_02)); + BOOST_TEST(((boost::multiprecision::fpclassify)(min_neg_03) == FP_ZERO) && (!signbit(min_neg_03))); + } + } } // namespace local auto main() -> int @@ -632,6 +731,7 @@ auto main() -> int std::cout << "Testing type: " << typeid(float_type).name() << std::endl; static_cast(local::test_edges()); + local::test_cpp_dec_float_rd_ovf_unf(); } { From 64c8c8d40b8fdd06707755cd29dbb844275b0891 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Fri, 4 Jul 2025 14:57:53 +0200 Subject: [PATCH 006/114] Try for more coverage --- .../boost/multiprecision/complex_adaptor.hpp | 73 ++++----- .../boost/multiprecision/cpp_dec_float.hpp | 139 ++++++++++-------- test/test_complex.cpp | 126 +++++++++++++++- test/test_various_edges_more.cpp | 7 +- 4 files changed, 240 insertions(+), 105 deletions(-) diff --git a/include/boost/multiprecision/complex_adaptor.hpp b/include/boost/multiprecision/complex_adaptor.hpp index d5edd917f..40ec4d74d 100644 --- a/include/boost/multiprecision/complex_adaptor.hpp +++ b/include/boost/multiprecision/complex_adaptor.hpp @@ -1,5 +1,7 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright 2018 John Maddock. Distributed under the Boost +// Copyright 2018 - 2025 John Maddock. +// Copyright 2025 Christopher Kormanyos. +// 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) @@ -7,13 +9,14 @@ #define BOOST_MP_COMPLEX_ADAPTOR_HPP #include -#include #include #include #include -#include + #include +#include #include +#include namespace boost { namespace multiprecision { @@ -479,37 +482,37 @@ inline void eval_sqrt(complex_adaptor& result, const complex_adaptor& result, const complex_adaptor __my_z_times_i, t1, t2, t3; - assign_components(__my_z_times_i, arg.imag_data(), arg.real_data()); - __my_z_times_i.real_data().negate(); + complex_adaptor my_z_times_i, t1, t2, t3; + assign_components(my_z_times_i, arg.imag_data(), arg.real_data()); + my_z_times_i.real_data().negate(); - eval_add(t1, __my_z_times_i, one); + eval_add(t1, my_z_times_i, one); eval_log(t2, t1); - eval_subtract(t1, one, __my_z_times_i); + eval_subtract(t1, one, my_z_times_i); eval_log(t3, t1); eval_subtract(t1, t3, t2); @@ -881,15 +884,15 @@ inline void eval_acosh(complex_adaptor& result, const complex_adaptor __my_zp(arg); - eval_add(__my_zp.real_data(), one); - complex_adaptor __my_zm(arg); - eval_subtract(__my_zm.real_data(), one); + complex_adaptor my_zp(arg); + eval_add(my_zp.real_data(), one); + complex_adaptor my_zm(arg); + eval_subtract(my_zm.real_data(), one); complex_adaptor t1, t2; - eval_divide(t1, __my_zm, __my_zp); + eval_divide(t1, my_zm, my_zp); eval_sqrt(t2, t1); - eval_multiply(t2, __my_zp); + eval_multiply(t2, my_zp); eval_add(t2, arg); eval_log(result, t2); } diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index b2604dd45..dade69830 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -505,23 +505,53 @@ class cpp_dec_float exponent_type order() const { const bool bo_order_is_zero = ((!(isfinite)()) || (data[0] == static_cast(0u))); + + exponent_type prefix = limb_order(data[static_cast(UINT8_C(0))]); + + return (bo_order_is_zero ? static_cast(0) : static_cast(exp + prefix)); + } + + #ifndef BOOST_MP_STANDALONE + template + void serialize(Archive& ar, const unsigned int /*version*/) + { + for (unsigned i = 0; i < data.size(); ++i) + ar & boost::make_nvp("digit", data[i]); + + ar & boost::make_nvp("exponent", exp); + ar & boost::make_nvp("sign", neg); + ar & boost::make_nvp("class-type", fpclass); + ar & boost::make_nvp("precision", prec_elem); + } + #endif + + private: + static bool data_elem_is_non_zero_predicate(const std::uint32_t& d) { return (d != static_cast(0u)); } + static bool data_elem_is_non_nine_predicate(const std::uint32_t& d) { return (d != static_cast(cpp_dec_float::cpp_dec_float_elem_mask - 1)); } + static bool char_is_nonzero_predicate(const char& c) { return (c != static_cast('0')); } + + // Inversion. + cpp_dec_float& calculate_inv(); + + static exponent_type limb_order(const limb_type limb) + { // // Binary search to find the order of the leading term: // exponent_type prefix = 0; - if (data[0] >= 100000UL) + if (limb >= 100000UL) { - if (data[0] >= 10000000UL) + if (limb >= 10000000UL) { - if (data[0] >= 100000000UL) + if (limb >= 100000000UL) prefix = 8; else prefix = 7; } else { - if (data[0] >= 1000000UL) + if (limb >= 1000000UL) prefix = 6; else prefix = 5; @@ -529,46 +559,24 @@ class cpp_dec_float } else { - if (data[0] >= 1000UL) + if (limb >= 1000UL) { - if (data[0] >= 10000UL) + if (limb >= 10000UL) prefix = 4; else prefix = 3; } else { - if (data[0] >= 100) + if (limb >= 100) prefix = 2; - else if (data[0] >= 10) + else if (limb >= 10) prefix = 1; } } - return (bo_order_is_zero ? static_cast(0) : static_cast(exp + prefix)); - } - - #ifndef BOOST_MP_STANDALONE - template - void serialize(Archive& ar, const unsigned int /*version*/) - { - for (unsigned i = 0; i < data.size(); ++i) - ar & boost::make_nvp("digit", data[i]); - - ar & boost::make_nvp("exponent", exp); - ar & boost::make_nvp("sign", neg); - ar & boost::make_nvp("class-type", fpclass); - ar & boost::make_nvp("precision", prec_elem); + return prefix; } - #endif - - private: - static bool data_elem_is_non_zero_predicate(const std::uint32_t& d) { return (d != static_cast(0u)); } - static bool data_elem_is_non_nine_predicate(const std::uint32_t& d) { return (d != static_cast(cpp_dec_float::cpp_dec_float_elem_mask - 1)); } - static bool char_is_nonzero_predicate(const char& c) { return (c != static_cast('0')); } - - // Inversion. - cpp_dec_float& calculate_inv(); bool isone() const { @@ -1531,49 +1539,52 @@ bool cpp_dec_float::isint() const template void cpp_dec_float::extract_parts(double& mantissa, ExponentType& exponent) const { - // Extract the approximate parts mantissa and base-10 exponent from the input cpp_dec_float value x. + // Extract the approximate parts mantissa and base-10 exponent from + // the input cpp_dec_float value x. + // This subroutine is designed to be fast and does not round. - // Extracts the mantissa and exponent. - exponent = exp; + // Extract the mantissa and exponent. - std::uint32_t p10 = static_cast(1u); - std::uint32_t test = data[0u]; + using local_exponent_type = ExponentType; - for (;;) - { - test /= static_cast(10u); + std::size_t index { static_cast(UINT8_C(0)) }; - if (test == static_cast(0u)) - { - break; - } + const local_exponent_type my_order_limb0 { limb_order(data[index]) }; - p10 *= static_cast(10u); - ++exponent; - } + exponent = static_cast(exp + my_order_limb0); - // Establish the upper bound of limbs for extracting the double. - const int max_elem_in_double_count = static_cast(static_cast(std::numeric_limits::digits10) / cpp_dec_float_elem_digits10) + (static_cast(static_cast(std::numeric_limits::digits10) % cpp_dec_float_elem_digits10) != 0 ? 1 : 0) + 1; + // Extract into the mantissa the first limb, extracted as a double. - // And make sure this upper bound stays within bounds of the elems. - const std::size_t max_elem_extract_count = static_cast((std::min)(static_cast(max_elem_in_double_count), cpp_dec_float_elem_number)); + mantissa = ((!neg) ? static_cast(data[index]) : -static_cast(data[index])); - // Extract into the mantissa the first limb, extracted as a double. - mantissa = static_cast(data[0]); - double scale = 1.0; + ++index; - // Extract the rest of the mantissa piecewise from the limbs. - for (std::size_t i = 1u; i < max_elem_extract_count; i++) + int digit_counter { }; + + double p10 { 1.0 }; + + // Scale the first limb with its order. + for ( ; digit_counter < static_cast(my_order_limb0); ++digit_counter) { - scale /= static_cast(cpp_dec_float_elem_mask); - mantissa += (static_cast(data[i]) * scale); + mantissa /= 10.0; + + p10 *= 10.0; } - mantissa /= static_cast(p10); + // Extract the rest of the mantissa piecewise from the limbs. + // Keep a running power-of-ten scale in the variable p10. + // This loop does not round. - if (neg) + while ( (digit_counter < std::numeric_limits::max_digits10 + 2) + && (index < static_cast(cpp_dec_float_elem_number))) { - mantissa = -mantissa; + p10 *= static_cast(cpp_dec_float_elem_mask); + + mantissa += static_cast(static_cast(data[index]) / p10); + + ++index; + + digit_counter += static_cast(cpp_dec_float_elem_digits10); } } @@ -1591,12 +1602,13 @@ double cpp_dec_float::extract_double() const } else { - return ((!neg) ? std::numeric_limits::infinity() + return ((!neg) ? +std::numeric_limits::infinity() : -std::numeric_limits::infinity()); } } cpp_dec_float xx(*this); + if (xx.isneg()) xx.negate(); @@ -1609,11 +1621,12 @@ double cpp_dec_float::extract_double() const // Check if *this cpp_dec_float exceeds the maximum of double. if (xx.compare(double_max()) > 0) { - return ((!neg) ? std::numeric_limits::infinity() + return ((!neg) ? +std::numeric_limits::infinity() : -std::numeric_limits::infinity()); } - std::stringstream strm; + std::stringstream strm { }; + strm.imbue(std::locale::classic()); strm << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); diff --git a/test/test_complex.cpp b/test/test_complex.cpp index 86b7cfaa4..b89ab45bb 100644 --- a/test/test_complex.cpp +++ b/test/test_complex.cpp @@ -9,34 +9,72 @@ // "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations", // in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469 +#include + #include +#include +#include + #ifdef TEST_MPC #include #endif -#include -#include #ifdef BOOST_HAS_FLOAT128 #include #endif +#if 0 // Testing cpp_dec_float does not (yet) work. +#ifdef TEST_CPP_DEC_FLOAT +#include +#endif +#endif // Testing cpp_dec_float does not (yet) work. #ifdef TEST_CPP_DOUBLE_FLOAT #include #endif -#include #include // Note: include this AFTER the test-backends are defined +#include #include #include #include #include +#include #include +template auto my_zero() noexcept -> FloatType&; + namespace local { + +template +auto time_point() noexcept -> IntegralTimePointType +{ + using local_integral_time_point_type = IntegralTimePointType; + using local_clock_type = ClockType; + + typename local_clock_type::time_point tp_zero { }; + + const auto my_now = local_clock_type::now(); + + const auto duration { my_now - tp_zero }; + + const std::uintmax_t + value + { + static_cast + ( + std::chrono::duration_cast(my_now - tp_zero).count() + ) + }; + + return static_cast(value); +} + template void test() { + std::cout << "Testing type: " << typeid(complex_type).name() << std::endl; - typedef typename complex_type::value_type float_type; + using float_type = typename complex_type::value_type; const std::string str_tol("0." + std::string(std::size_t(std::numeric_limits::digits10 - 2), char('0')) + std::string(std::size_t(2U), char('9'))); @@ -158,6 +196,72 @@ void test() BOOST_CHECK_CLOSE_FRACTION(result_26.imag(), control_26.imag(), tol); BOOST_CHECK_CLOSE_FRACTION(abs(z1), boost::lexical_cast("3.60555127546398929311922126747049594625129657384524621271045305622716694829301044520461908201849071767351418202406"), tol) + + { + std::mt19937_64 gen { time_point() }; + + auto dis = + std::uniform_real_distribution + { + static_cast(1.1F), + static_cast(1.5E10F) + }; + + float_type around_three_base("3.60555127546398929311922126747049594625129657384524621271045305622716694829301044520461908201849071767351418202406"); + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) + { + static_cast(index); + + const float_type real_val = around_three_base * dis(gen); + + const complex_type cpx(real_val); + + { + const complex_type sqrt_cpx = sqrt(cpx); + const float_type sqrt_ctrl = sqrt(real_val); + + BOOST_CHECK_CLOSE_FRACTION(sqrt_cpx.real(), sqrt_ctrl, tol); + BOOST_TEST((boost::multiprecision::fpclassify)(sqrt_cpx.imag()) == FP_ZERO); + } + + { + const complex_type exp_cpx = exp(cpx); + const float_type exp_ctrl = exp(real_val); + + BOOST_CHECK_CLOSE_FRACTION(exp_cpx.real(), exp_ctrl, tol); + BOOST_TEST((boost::multiprecision::fpclassify)(exp_cpx.imag()) == FP_ZERO); + } + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) + { + static_cast(index); + + const complex_type cpx(around_three_base * dis(gen), around_three_base * dis(gen)); + + const complex_type cpx_zero(::my_zero() * dis(gen), ::my_zero() * dis(gen)); + const complex_type cpx_result = pow(cpx, cpx_zero); + + const bool result_pow_zero_is_ok + { + (cpx_result.real() == 1) + && ((boost::multiprecision::fpclassify)(cpx_result.imag()) == FP_ZERO) + }; + + BOOST_TEST(result_pow_zero_is_ok); + + const complex_type cpx_asin_zero = asin(cpx_zero); + + const bool result_asin_zero_is_ok + { + ((boost::multiprecision::fpclassify)(cpx_asin_zero.real()) == FP_ZERO) + && ((boost::multiprecision::fpclassify)(cpx_asin_zero.imag()) == FP_ZERO) + }; + + BOOST_TEST(result_asin_zero_is_ok); + } + } } } // namespace local @@ -173,6 +277,18 @@ int main() local::test(); #endif +#if 0 // Testing cpp_dec_float does not (yet) work. +#ifdef TEST_CPP_DEC_FLOAT + { + using dec_float_backend_type = boost::multiprecision::cpp_dec_float<50>; + + using dec_float_type = boost::multiprecision::number; + + local::test(); + } +#endif +#endif // Testing cpp_dec_float does not (yet) work. + #if defined(TEST_CPP_DOUBLE_FLOAT) { local::test>, boost::multiprecision::et_off>>(); @@ -185,3 +301,5 @@ int main() return boost::report_errors(); } + +template auto my_zero() noexcept -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index 46bd34cf4..98ca76661 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -126,9 +126,9 @@ namespace local for(std::size_t index { std::size_t { UINT8_C(0) }}; index < std::tuple_size::value; ++index) { - const any_float_type start(any_float_type(float_number_strings[index]) * (dis(gen)* dis(gen))); - const other_float_type other(start); - const any_float_type backto(other); + const any_float_type start(boost::lexical_cast(float_number_strings[index]) * (dis(gen)* dis(gen))); + const other_float_type other(static_cast(start)); + const any_float_type backto(static_cast(other)); const bool result_of_trip_is_ok @@ -732,6 +732,7 @@ auto main() -> int static_cast(local::test_edges()); local::test_cpp_dec_float_rd_ovf_unf(); + local::test_convert_and_back(0.0F); } { From be9c643032474f9dab60026e54fa604abf8840ae Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Fri, 4 Jul 2025 19:58:46 +0200 Subject: [PATCH 007/114] Try for even more cover lines --- .../boost/multiprecision/cpp_dec_float.hpp | 29 ++++++++++------- test/test_arithmetic.hpp | 16 +++++----- test/test_cpp_bin_float_io.cpp | 6 ++++ test/test_various_edges_more.cpp | 32 +++++++++++++++++++ 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index dade69830..7014e12b5 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -2017,12 +2017,14 @@ std::string cpp_dec_float::str(std::intmax_t const std::uint32_t ix = number_of_digits == 0 ? 0 : static_cast(static_cast(my_str[static_cast(number_of_digits - 1)]) - static_cast('0')); if ((ix & 1u) == 0) { - // We have an even digit followed by a 5, so we might not actually need to round up - // if all the remaining digits are zero: + // We have an even digit followed by a 5, so we might not actually + // need to round up if all the remaining digits are zero: if (my_str.find_first_not_of('0', static_cast(number_of_digits + 1)) == std::string::npos) { bool all_zeros = true; - // No none-zero trailing digits in the string, now check whatever parts we didn't convert to the string: + + // There are no non-zero trailing digits in the string, + // now check whatever parts we didn't convert to the string: for (std::size_t i = number_of_elements; i < data.size(); i++) { if (data[i]) @@ -2139,11 +2141,12 @@ bool cpp_dec_float::rd_string(const char* con // Get a possible +/- sign and remove it. neg = false; - if (my_str.size()) + if (!my_str.empty()) { if (my_str[0] == '-') { neg = true; + my_str.erase(0, 1); } else if (my_str[0] == '+') @@ -2151,23 +2154,25 @@ bool cpp_dec_float::rd_string(const char* con my_str.erase(0, 1); } } + // // Special cases for infinities and NaN's: // if ((my_str == "inf") || (my_str == "INF") || (my_str == "infinity") || (my_str == "INFINITY")) { - if (neg) - { - *this = this->inf(); - this->negate(); - } - else - *this = this->inf(); + const bool tmp_neg { neg }; + + *this = this->inf(); + + neg = tmp_neg; + return true; } + if ((my_str.size() >= 3) && ((my_str.substr(0, 3) == "nan") || (my_str.substr(0, 3) == "NAN") || (my_str.substr(0, 3) == "NaN"))) { *this = this->nan(); + return true; } @@ -2485,7 +2490,7 @@ cpp_dec_float::cpp_dec_float(const double man d -= static_cast(n); d *= static_cast(cpp_dec_float_elem_mask); } -} +} // LCOV_EXCL_LINE template template diff --git a/test/test_arithmetic.hpp b/test/test_arithmetic.hpp index ffde719c4..7d7cd1b7e 100644 --- a/test/test_arithmetic.hpp +++ b/test/test_arithmetic.hpp @@ -3045,45 +3045,45 @@ void test_basic_conditionals(Real a, Real b) { if (a) { - BOOST_ERROR("Unexpected non-zero result"); + BOOST_ERROR("Unexpected non-zero result"); // LCOV_EXCL_LINE } if (!a) { } else { - BOOST_ERROR("Unexpected zero result"); + BOOST_ERROR("Unexpected zero result"); // LCOV_EXCL_LINE } b = 2; if (!b) { - BOOST_ERROR("Unexpected zero result"); + BOOST_ERROR("Unexpected zero result"); // LCOV_EXCL_LINE } if (b) { } else { - BOOST_ERROR("Unexpected non-zero result"); + BOOST_ERROR("Unexpected non-zero result"); // LCOV_EXCL_LINE } if (a && b) { - BOOST_ERROR("Unexpected zero result"); + BOOST_ERROR("Unexpected zero result"); // LCOV_EXCL_LINE } if (!(a || b)) { - BOOST_ERROR("Unexpected zero result"); + BOOST_ERROR("Unexpected zero result"); // LCOV_EXCL_LINE } if (a + b) { } else { - BOOST_ERROR("Unexpected zero result"); + BOOST_ERROR("Unexpected zero result"); // LCOV_EXCL_LINE } if (b - 2) { - BOOST_ERROR("Unexpected non-zero result"); + BOOST_ERROR("Unexpected non-zero result"); // LCOV_EXCL_LINE } } diff --git a/test/test_cpp_bin_float_io.cpp b/test/test_cpp_bin_float_io.cpp index ee22bd151..378eb29e6 100644 --- a/test/test_cpp_bin_float_io.cpp +++ b/test/test_cpp_bin_float_io.cpp @@ -44,6 +44,7 @@ struct stopwatch typename Clock::time_point m_start; }; +// LCOV_EXCL_START void print_flags(std::ios_base::fmtflags f) { std::cout << "Formatting flags were: "; @@ -57,6 +58,7 @@ void print_flags(std::ios_base::fmtflags f) std::cout << "showpos "; std::cout << std::endl; } +// LCOV_EXCL_STOP template void test() @@ -88,6 +90,7 @@ void test() const char* expect = string_data[j][col]; if (ss.str() != expect) { + // LCOV_EXCL_START std::cout << std::setprecision(20) << "Testing value " << val << std::endl; print_flags(f[i]); std::cout << "Precision is: " << prec << std::endl; @@ -95,6 +98,7 @@ void test() std::cout << "Expected: " << expect << std::endl; ++boost::detail::test_errors(); mp_t(val).str(prec, f[i]); // for debugging + // LCOV_EXCL_STOP } } } @@ -120,6 +124,7 @@ void test() const char* expect = zeros[col]; if (ss.str() != expect) { + // LCOV_EXCL_START std::cout << std::setprecision(20) << "Testing value " << val << std::endl; print_flags(f[i]); std::cout << "Precision is: " << prec << std::endl; @@ -127,6 +132,7 @@ void test() std::cout << "Expected: " << expect << std::endl; ++boost::detail::test_errors(); mp_t(val).str(prec, f[i]); // for debugging + // LCOV_EXCL_STOP } } } diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index 98ca76661..c1950f1a4 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -358,6 +358,38 @@ namespace local result_is_ok = (result_funky_strings_is_ok && result_is_ok); } + { + const std::initializer_list + infty_strings_list + { + std::string("inf"), std::string("INF"), std::string("infinity"), std::string("INFINITY"), + std::string("+inf"), std::string("+INF"), std::string("+infinity"), std::string("+INFINITY"), + std::string("-inf"), std::string("-INF"), std::string("-infinity"), std::string("-INFINITY") + }; + + const std::vector infty_strings(infty_strings_list); + + std::size_t infty_count { }; + + for(const auto& str : infty_strings) + { + const float_type flt_from_inf_string(str); + + const bool + result_infty_strings_is_ok + { + (boost::multiprecision::isinf)(flt_from_inf_string) + && ((infty_count < std::size_t { UINT8_C(8) }) ? (!signbit(flt_from_inf_string)) : signbit(flt_from_inf_string)) + }; + + ++infty_count; + + BOOST_TEST(result_infty_strings_is_ok); + + result_is_ok = (result_infty_strings_is_ok && result_is_ok); + } + } + { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { From af8f932b694f721880c73f174a881ca6775c8fc3 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Fri, 4 Jul 2025 20:01:28 +0200 Subject: [PATCH 008/114] Remove an unwanted compiler warning --- test/test_various_edges_more.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index c1950f1a4..dde4531c7 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -650,8 +650,6 @@ namespace local using local_cpp_dec_float_type = CppDecFloatType; { - typename local_cpp_dec_float_type::backend_type::exponent_type max_exp { }; - std::string str_max_pos_01("+1.0E+"); std::string str_max_pos_02("+2.0E+"); std::string str_max_pos_03("+3.0E+"); From 7012b7dd02cdb3dbe34fe7394a8434f89576644e Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sat, 5 Jul 2025 10:53:46 +0200 Subject: [PATCH 009/114] Improve cpp_dec_float and test_exp --- .../boost/multiprecision/cpp_dec_float.hpp | 145 ++++++------------ test/test_exp.cpp | 18 ++- 2 files changed, 60 insertions(+), 103 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 7014e12b5..72375a76d 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -1,6 +1,7 @@ /////////////////////////////////////////////////////////////////////////////// // Copyright Christopher Kormanyos 2002 - 2025. -// Copyright 2011 -2025 John Maddock. Distributed under the Boost +// Copyright John Maddock 2011 -2025. +// 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) // @@ -8,27 +9,10 @@ // "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations", // in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469 // -// There are some "noexcept" specifications on the functions in this file. -// Unlike in pre-C++11 versions, compilers can now detect noexcept misuse -// at compile time, allowing for simple use of it here. -// #ifndef BOOST_MP_CPP_DEC_FLOAT_HPP #define BOOST_MP_CPP_DEC_FLOAT_HPP -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -57,6 +41,20 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #ifdef BOOST_MSVC #pragma warning(push) #pragma warning(disable : 6326) // comparison of two constants @@ -135,18 +133,14 @@ class cpp_dec_float cpp_dec_float_NaN } fpclass_type; - array_type data; - exponent_type exp; - bool neg; - fpclass_type fpclass; - std::int32_t prec_elem; + array_type data { }; + exponent_type exp { }; + bool neg { }; + fpclass_type fpclass { cpp_dec_float_finite }; + std::int32_t prec_elem { cpp_dec_float_elem_number }; // Private constructor from the floating-point class type. - explicit cpp_dec_float(fpclass_type c) : data(), - exp(static_cast(0)), - neg(false), - fpclass(c), - prec_elem(cpp_dec_float_elem_number) {} + explicit cpp_dec_float(fpclass_type c) : fpclass(c) { } // Constructor from an initializer_list, an optional // (value-aligned) exponent and a Boolean sign. @@ -167,29 +161,16 @@ class cpp_dec_float public: // Public Constructors - cpp_dec_float() noexcept(noexcept(array_type())) : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) {} - - cpp_dec_float(const char* s) : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) + cpp_dec_float() noexcept(noexcept(array_type())) { } + + cpp_dec_float(const char* s) { *this = s; } template - cpp_dec_float(I i, - typename std::enable_if::value && (sizeof(I) <= sizeof(long long))>::type* = nullptr) - : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) + cpp_dec_float(I i, typename std::enable_if<( boost::multiprecision::detail::is_unsigned::value + && (sizeof(I) <= sizeof(long long)))>::type* = nullptr) { from_unsigned_long_long(i); } @@ -199,11 +180,6 @@ class cpp_dec_float typename std::enable_if<( boost::multiprecision::detail::is_signed::value && boost::multiprecision::detail::is_integral::value && (sizeof(I) <= sizeof(long long)))>::type* = nullptr) - : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) { if (i < 0) { @@ -218,38 +194,30 @@ class cpp_dec_float : data(f.data), exp(f.exp), neg(f.neg), - fpclass(f.fpclass), + fpclass(static_cast(static_cast(f.fpclass))), prec_elem(f.prec_elem) {} template cpp_dec_float(const cpp_dec_float& f, typename std::enable_if::type* = nullptr) - : data(), - exp(f.exp), + : exp(f.exp), neg(f.neg), - fpclass(static_cast(static_cast(f.fpclass))), - prec_elem(cpp_dec_float_elem_number) + fpclass(static_cast(static_cast(f.fpclass))) { std::copy(f.data.begin(), f.data.begin() + f.prec_elem, data.begin()); } + template explicit cpp_dec_float(const cpp_dec_float& f, typename std::enable_if< !(D <= Digits10)>::type* = nullptr) - : data(), - exp(f.exp), + : exp(f.exp), neg(f.neg), - fpclass(static_cast(static_cast(f.fpclass))), - prec_elem(cpp_dec_float_elem_number) + fpclass(static_cast(static_cast(f.fpclass))) { // TODO: this doesn't round! std::copy(f.data.begin(), f.data.begin() + prec_elem, data.begin()); } template - cpp_dec_float(const F val, typename std::enable_if::value - >::type* = nullptr) : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) + cpp_dec_float(const F val, typename std::enable_if::value>::type* = nullptr) { *this = val; } @@ -1547,42 +1515,32 @@ void cpp_dec_float::extract_parts(double& man using local_exponent_type = ExponentType; - std::size_t index { static_cast(UINT8_C(0)) }; - - const local_exponent_type my_order_limb0 { limb_order(data[index]) }; + const local_exponent_type my_order_limb0 { limb_order(data.front()) }; exponent = static_cast(exp + my_order_limb0); // Extract into the mantissa the first limb, extracted as a double. - mantissa = ((!neg) ? static_cast(data[index]) : -static_cast(data[index])); - - ++index; + mantissa = ((!neg) ? static_cast(data.front()) : -static_cast(data.front())); - int digit_counter { }; + int digit_counter { static_cast(my_order_limb0) }; - double p10 { 1.0 }; + // Keep a running power-of-ten scale in the variable p10. + double p10 { static_cast(detail::pow10_maker(static_cast(my_order_limb0))) }; // Scale the first limb with its order. - for ( ; digit_counter < static_cast(my_order_limb0); ++digit_counter) - { - mantissa /= 10.0; - - p10 *= 10.0; - } + mantissa /= p10; // Extract the rest of the mantissa piecewise from the limbs. - // Keep a running power-of-ten scale in the variable p10. // This loop does not round. - while ( (digit_counter < std::numeric_limits::max_digits10 + 2) - && (index < static_cast(cpp_dec_float_elem_number))) + auto itr_data = data.cbegin() + static_cast(UINT8_C(1)); + + while ((itr_data != data.cend()) && (digit_counter <= std::numeric_limits::max_digits10)) { p10 *= static_cast(cpp_dec_float_elem_mask); - mantissa += static_cast(static_cast(data[index]) / p10); - - ++index; + mantissa += static_cast(static_cast(*itr_data++) / p10); digit_counter += static_cast(cpp_dec_float_elem_digits10); } @@ -1629,7 +1587,7 @@ double cpp_dec_float::extract_double() const strm.imbue(std::locale::classic()); - strm << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); + strm << str(std::numeric_limits::max_digits10, std::ios_base::scientific); double d; strm >> d; @@ -1676,7 +1634,7 @@ long double cpp_dec_float::extract_long_doubl std::stringstream strm; strm.imbue(std::locale::classic()); - strm << str(std::numeric_limits::digits10 + (2 + 1), std::ios_base::scientific); + strm << str(std::numeric_limits::max_digits10, std::ios_base::scientific); long double ld; strm >> ld; @@ -2434,11 +2392,6 @@ bool cpp_dec_float::rd_string(const char* con template cpp_dec_float::cpp_dec_float(const double mantissa, const ExponentType exponent) - : data(), - exp(static_cast(0)), - neg(false), - fpclass(cpp_dec_float_finite), - prec_elem(cpp_dec_float_elem_number) { // Create *this cpp_dec_float from a given mantissa and exponent. // Note: This constructor does not maintain the full precision of double. @@ -2496,9 +2449,11 @@ template template typename std::enable_if::value, cpp_dec_float&>::type cpp_dec_float::operator=(Float a) { - // Christopher Kormanyos's original code used a cast to long long here, but that fails - // when long double has more digits than a long long. + // Christopher Kormanyos's original code used a cast to long long here, + // but that fails when long double has more digits than a long long. + BOOST_MP_FLOAT128_USING + using std::floor; using std::frexp; using std::ldexp; diff --git a/test/test_exp.cpp b/test/test_exp.cpp index 6cc7f1d16..92eaf7419 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -75,7 +75,7 @@ void test() unsigned max_err = 0; - BOOST_IF_CONSTEXPR (!::has_poor_exp_range_or_precision_support::value) + BOOST_IF_CONSTEXPR (std::numeric_limits::is_specialized && (std::numeric_limits::max_exponent10 > 4000)) { static const std::array data = {{ @@ -159,7 +159,7 @@ void test() #else BOOST_TEST(max_err < 5000); #endif - } // !::has_poor_exp_range_or_precision_support::value + } // (std::numeric_limits::max_exponent10 > 4000) using std::ldexp; @@ -205,8 +205,11 @@ void test() if (!boost::multiprecision::is_interval_number::value) { T bug_case = -1.05 * log((std::numeric_limits::max)()); - for (unsigned i = 0; bug_case > -20 / std::numeric_limits::epsilon(); ++i, bug_case *= 1.05) + + for (unsigned i = 0U; bug_case > -20 / std::numeric_limits::epsilon(); ++i, bug_case *= 1.05) { + static_cast(i); + if (std::numeric_limits::has_infinity) { BOOST_CHECK_EQUAL(exp(bug_case), 0); @@ -217,20 +220,19 @@ void test() } } - // Handle uneven/asymmetric exponents on min/max of cpp_double_fp_backend BOOST_IF_CONSTEXPR (::has_poor_exp_range_or_precision_support::value) { - bug_case = log(1 / (std::numeric_limits::min)()) / -1.0005; + bug_case = log(T(1) / (std::numeric_limits::min)()) / -1.0005; } else { bug_case = log((std::numeric_limits::max)()) / -1.0005; } - unsigned i { 0U }; - - for ( ; i < 20U; ++i, bug_case /= static_cast(1.05L)) + for (unsigned i { 0U }; i < 20U; ++i, bug_case /= static_cast(1.05L)) { + static_cast(i); + BOOST_CHECK_GE(exp(bug_case), (std::numeric_limits::min)()); } } From 0d255d014a0268cd8097d6f8766a5fb2bae9bb80 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sat, 5 Jul 2025 14:05:19 +0200 Subject: [PATCH 010/114] Add more dec_float frexp tests --- .../boost/multiprecision/cpp_dec_float.hpp | 78 +++++++++++++------ test/test_various_edges_more.cpp | 50 +++++++++++- 2 files changed, 103 insertions(+), 25 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 72375a76d..8583bea92 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -3400,34 +3400,50 @@ inline void eval_ldexp(cpp_dec_float& result, { const long long the_exp = static_cast(e); - if ((the_exp > (std::numeric_limits::exponent_type>::max)()) || (the_exp < (std::numeric_limits::exponent_type>::min)())) + using local_cpp_dec_float_type = cpp_dec_float; + using local_exponent_type = typename local_cpp_dec_float_type::exponent_type; + + using local_common_type = typename std::common_type::type; + + if ( (static_cast(the_exp) > static_cast((std::numeric_limits::max)())) + || (static_cast(the_exp) < static_cast((std::numeric_limits::min)()))) + { BOOST_MP_THROW_EXCEPTION(std::runtime_error(std::string("Exponent value is out of range."))); + } result = x; - if ((the_exp > static_cast(-std::numeric_limits::digits)) && (the_exp < static_cast(0))) + if ( (static_cast(the_exp) > static_cast(-std::numeric_limits::digits)) + && (static_cast(the_exp) < static_cast(0))) + { result.div_unsigned_long_long(1ULL << static_cast(-the_exp)); - else if ((the_exp < static_cast(std::numeric_limits::digits)) && (the_exp > static_cast(0))) + } + else if ( (static_cast(the_exp) < static_cast(std::numeric_limits::digits)) + && (static_cast(the_exp) > static_cast(0))) + { result.mul_unsigned_long_long(1ULL << the_exp); + } else if (the_exp != static_cast(0)) { - if ((the_exp < cpp_dec_float::cpp_dec_float_min_exp / 2) && (x.order() > 0)) + if ((the_exp < local_cpp_dec_float_type::cpp_dec_float_min_exp / 2) && (x.order() > 0)) { long long half_exp = e / 2; - cpp_dec_float t = cpp_dec_float::pow2(half_exp); + local_cpp_dec_float_type t { local_cpp_dec_float_type::pow2(half_exp) }; result *= t; if (2 * half_exp != e) t *= 2; result *= t; - } + } // LCOV_EXCL_LINE else - result *= cpp_dec_float::pow2(e); + result *= local_cpp_dec_float_type::pow2(e); } } template inline void eval_frexp(cpp_dec_float& result, const cpp_dec_float& x, ExponentType* e) { + const bool b_neg { x.isneg() }; + result = x; if (result.iszero() || (result.isinf)() || (result.isnan)()) @@ -3436,12 +3452,16 @@ inline void eval_frexp(cpp_dec_float& result, return; } - if (result.isneg()) + if (b_neg) result.negate(); - typename cpp_dec_float::exponent_type t = result.order(); + using local_cpp_dec_float_type = cpp_dec_float; + using local_exponent_type = typename local_cpp_dec_float_type::exponent_type; + + local_exponent_type t { result.order() }; + BOOST_MP_USING_ABS - if (abs(t) < ((std::numeric_limits::exponent_type>::max)() / 1000)) + if (abs(t) < ((std::numeric_limits::max)() / 1000)) { t *= 1000; t /= 301; @@ -3452,55 +3472,65 @@ inline void eval_frexp(cpp_dec_float& result, t *= 1000; } - result *= cpp_dec_float::pow2(-t); + result *= local_cpp_dec_float_type::pow2(-t); if (result.iszero() || (result.isinf)() || (result.isnan)()) { // pow2 overflowed, slip the calculation up: result = x; - if (result.isneg()) + if (b_neg) result.negate(); t /= 2; - result *= cpp_dec_float::pow2(-t); + result *= local_cpp_dec_float_type::pow2(-t); } BOOST_MP_USING_ABS if (abs(result.order()) > 5) { // If our first estimate doesn't get close enough then try recursion until we do: - typename cpp_dec_float::exponent_type e2; - cpp_dec_float r2; + local_exponent_type e2; + cpp_dec_float r2; eval_frexp(r2, result, &e2); // overflow protection: - if ((t > 0) && (e2 > 0) && (t > (std::numeric_limits::exponent_type>::max)() - e2)) + if ((t > 0) && (e2 > 0) && (t > (std::numeric_limits::max)() - e2)) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); - if ((t < 0) && (e2 < 0) && (t < (std::numeric_limits::exponent_type>::min)() - e2)) + if ((t < 0) && (e2 < 0) && (t < (std::numeric_limits::min)() - e2)) BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); t += e2; result = r2; } - while (result.compare(cpp_dec_float::one()) >= 0) + while (result.compare(local_cpp_dec_float_type::one()) >= 0) { - result /= cpp_dec_float::two(); + result /= local_cpp_dec_float_type::two(); ++t; } - while (result.compare(cpp_dec_float::half()) < 0) + while (result.compare(local_cpp_dec_float_type::half()) < 0) { - result *= cpp_dec_float::two(); + result *= local_cpp_dec_float_type::two(); --t; } *e = t; - if (x.isneg()) + if (b_neg) result.negate(); } template inline typename std::enable_if< !std::is_same::value>::type eval_frexp(cpp_dec_float& result, const cpp_dec_float& x, int* e) { - typename cpp_dec_float::exponent_type t; + using local_exponent_type = typename cpp_dec_float::exponent_type; + + local_exponent_type t { }; + eval_frexp(result, x, &t); - if ((t > (std::numeric_limits::max)()) || (t < (std::numeric_limits::min)())) + + using local_common_type = typename std::common_type::type; + + if ( (static_cast(t) > static_cast((std::numeric_limits::max)())) + || (static_cast(t) < static_cast((std::numeric_limits::min)()))) + { BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is outside the range of an int")); + } + *e = static_cast(t); } diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index dde4531c7..8e26f07aa 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -643,7 +643,6 @@ namespace local return result_of_trip_is_ok; } - template auto test_cpp_dec_float_rd_ovf_unf() -> void { @@ -739,6 +738,54 @@ namespace local BOOST_TEST(((boost::multiprecision::fpclassify)(min_neg_03) == FP_ZERO) && (!signbit(min_neg_03))); } } + + template + auto test_cpp_dec_float_frexp_edge() -> void + { + using float_type = CppDecFloatType; + + using float_backend_ctrl_type = boost::multiprecision::cpp_bin_float<50, boost::multiprecision::digit_base_10, void, std::int32_t>; + + using float_ctrl_type = boost::multiprecision::number; + + std::mt19937_64 gen { time_point() }; + + auto dis = + std::uniform_real_distribution + { + static_cast(1.01F), + static_cast(1.04F) + }; + + { + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(32)); ++index) + { + static_cast(index); + + using local_exponent_type = typename float_type::backend_type::exponent_type; + + local_exponent_type exp_val { }; + std::int32_t exp_val_ctrl { }; + + float_type arg_near_max { (std::numeric_limits::max)() / dis(gen) }; + + const bool is_neg { ((index & 1U) != 0U) }; + + if(is_neg) + { + arg_near_max = -arg_near_max; + } + + float_type frexp_near_max { frexp(arg_near_max, &exp_val) }; + const float_ctrl_type frexp_near_max_ctrl { frexp(float_ctrl_type(arg_near_max), &exp_val_ctrl) }; + + BOOST_TEST((boost::multiprecision::isfinite)(frexp_near_max)); + BOOST_TEST(exp_val >= (std::numeric_limits::max_exponent - 8)); + BOOST_TEST(exp_val == exp_val_ctrl); + BOOST_TEST(is_close_fraction(float_type(frexp_near_max_ctrl), frexp_near_max, std::numeric_limits::epsilon() * 8)); + } + } + } } // namespace local auto main() -> int @@ -763,6 +810,7 @@ auto main() -> int static_cast(local::test_edges()); local::test_cpp_dec_float_rd_ovf_unf(); local::test_convert_and_back(0.0F); + local::test_cpp_dec_float_frexp_edge(); } { From 885e2d9a973cf0ac0064586841ffa994050eebec Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sat, 5 Jul 2025 15:51:57 +0200 Subject: [PATCH 011/114] Finish dec_float coverage for now --- .../boost/multiprecision/cpp_dec_float.hpp | 52 +++++++++++-------- test/test_various_edges_more.cpp | 4 +- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 8583bea92..283c317d3 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -1948,13 +1948,13 @@ std::string cpp_dec_float::str(std::intmax_t if (number_of_digits) { my_str.insert(static_cast(0), std::string::size_type(number_of_digits), '0'); - have_leading_zeros = true; + have_leading_zeros = true; // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. } } if (number_of_digits < 0) { - my_str = "0"; + my_str = "0"; // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. if (isneg()) my_str.insert(static_cast(0), 1, '-'); boost::multiprecision::detail::format_float_string(my_str, 0, number_of_digits - my_exp - 1, f, this->iszero()); @@ -2036,7 +2036,7 @@ std::string cpp_dec_float::str(std::intmax_t } else { - my_str = "1"; + my_str = "1"; // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. ++my_exp; } } @@ -2089,7 +2089,7 @@ bool cpp_dec_float::rd_string(const char* con exp = boost::lexical_cast(static_cast(my_str.c_str() + (pos + 1u))); #else if (my_str.find_first_not_of(valid_characters, ((my_str[pos + 1] == '+') || (my_str[pos + 1] == '-')) ? pos + 2 : pos + 1) != std::string::npos) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // LCOV_EXCL_LINE exp = static_cast(std::atoll(static_cast(my_str.c_str() + (pos + 1u)))); #endif @@ -2165,9 +2165,9 @@ bool cpp_dec_float::rd_string(const char* con { // Check we have only digits either side of the point: if (my_str.find_first_not_of(valid_characters) != pos) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // LCOV_EXCL_LINE if (my_str.find_first_not_of(valid_characters, pos + 1) != std::string::npos) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // LCOV_EXCL_LINE // Remove all trailing insignificant zeros. const std::string::const_reverse_iterator rit_non_zero = std::find_if(my_str.rbegin(), my_str.rend(), char_is_nonzero_predicate); @@ -2216,7 +2216,7 @@ bool cpp_dec_float::rd_string(const char* con { // We should have only digits: if (my_str.find_first_not_of(valid_characters) != std::string::npos) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with non-numeric content")); // LCOV_EXCL_LINE // Input string has no decimal point: Append decimal point. my_str.append("."); @@ -2280,7 +2280,7 @@ bool cpp_dec_float::rd_string(const char* con // Throws an error for a strange construction like 3.14L if(pos != std::string::npos && (my_str.back() == 'L' || my_str.back() == 'l' || my_str.back() == 'u' || my_str.back() == 'U')) { - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with an integer literal")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Can not construct a floating point with an integer literal")); // LCOV_EXCL_LINE } const std::int32_t n_dec = static_cast(static_cast(my_str.length() - 1u) - static_cast(pos)); @@ -2396,18 +2396,30 @@ cpp_dec_float::cpp_dec_float(const double man // Create *this cpp_dec_float from a given mantissa and exponent. // Note: This constructor does not maintain the full precision of double. - const bool mantissa_is_iszero = (::fabs(mantissa) < ((std::numeric_limits::min)() * (1.0 + std::numeric_limits::epsilon()))); + std::fill(data.begin(), data.end(), static_cast(0u)); + + constexpr double + dbl_min_check + { + static_cast + ( + (std::numeric_limits::min)() * (1.0 + std::numeric_limits::epsilon()) + ) + }; + + using std::fabs; + + const bool mantissa_is_iszero = (fabs(mantissa) < dbl_min_check); if (mantissa_is_iszero) { - std::fill(data.begin(), data.end(), static_cast(0u)); return; } - const bool b_neg = (mantissa < 0.0); + const bool b_neg { (mantissa < 0.0) }; - double d = ((!b_neg) ? mantissa : -mantissa); - exponent_type e = exponent; + double d { ((!b_neg) ? mantissa : -mantissa) }; + exponent_type e { exponent }; while (d > 10.0) { @@ -2431,8 +2443,6 @@ cpp_dec_float::cpp_dec_float(const double man exp = e; neg = b_neg; - std::fill(data.begin(), data.end(), static_cast(0u)); - constexpr std::int32_t digit_ratio = static_cast(static_cast(std::numeric_limits::digits10) / static_cast(cpp_dec_float_elem_digits10)); constexpr std::int32_t digit_loops = static_cast(digit_ratio + static_cast(2)); @@ -3408,7 +3418,7 @@ inline void eval_ldexp(cpp_dec_float& result, if ( (static_cast(the_exp) > static_cast((std::numeric_limits::max)())) || (static_cast(the_exp) < static_cast((std::numeric_limits::min)()))) { - BOOST_MP_THROW_EXCEPTION(std::runtime_error(std::string("Exponent value is out of range."))); + BOOST_MP_THROW_EXCEPTION(std::runtime_error(std::string("Exponent value is out of range."))); // LCOV_EXCL_LINE } result = x; @@ -3492,21 +3502,21 @@ inline void eval_frexp(cpp_dec_float& result, eval_frexp(r2, result, &e2); // overflow protection: if ((t > 0) && (e2 > 0) && (t > (std::numeric_limits::max)() - e2)) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); // LCOV_EXCL_LINE if ((t < 0) && (e2 < 0) && (t < (std::numeric_limits::min)() - e2)) - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is too large to be represented as a power of 2.")); // LCOV_EXCL_LINE t += e2; result = r2; } while (result.compare(local_cpp_dec_float_type::one()) >= 0) { - result /= local_cpp_dec_float_type::two(); + result.div_unsigned_long_long(2ULL); ++t; } while (result.compare(local_cpp_dec_float_type::half()) < 0) { - result *= local_cpp_dec_float_type::two(); + result.mul_unsigned_long_long(2ULL); --t; } *e = t; @@ -3528,7 +3538,7 @@ inline typename std::enable_if< !std::is_same::value>::type e if ( (static_cast(t) > static_cast((std::numeric_limits::max)())) || (static_cast(t) < static_cast((std::numeric_limits::min)()))) { - BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is outside the range of an int")); + BOOST_MP_THROW_EXCEPTION(std::runtime_error("Exponent is outside the range of an int")); // LCOV_EXCL_LINE } *e = static_cast(t); diff --git a/test/test_various_edges_more.cpp b/test/test_various_edges_more.cpp index 8e26f07aa..d37d95d9b 100644 --- a/test/test_various_edges_more.cpp +++ b/test/test_various_edges_more.cpp @@ -363,8 +363,8 @@ namespace local infty_strings_list { std::string("inf"), std::string("INF"), std::string("infinity"), std::string("INFINITY"), - std::string("+inf"), std::string("+INF"), std::string("+infinity"), std::string("+INFINITY"), - std::string("-inf"), std::string("-INF"), std::string("-infinity"), std::string("-INFINITY") + std::string("+inf"), std::string("+INF"), std::string("+infinity"), std::string("+INFINITY"), + std::string("-inf"), std::string("-INF"), std::string("-infinity"), std::string("-INFINITY") }; const std::vector infty_strings(infty_strings_list); From 40fede4b5dd511b28a41d4f4743d3b1efc6ee58d Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sat, 5 Jul 2025 20:23:44 +0200 Subject: [PATCH 012/114] Finish dec_float cover and comments --- include/boost/multiprecision/cpp_dec_float.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 283c317d3..382d741d6 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -70,7 +70,7 @@ struct number_category -class cpp_dec_float +class cpp_dec_float // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. { private: // Perform some static sanity checks. @@ -157,7 +157,7 @@ class cpp_dec_float a.prec_elem = cpp_dec_float_elem_number; return a; - } // LCOV_EXCL_LINE + } // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. public: // Public Constructors @@ -1167,7 +1167,7 @@ cpp_dec_float& cpp_dec_float(n); @@ -2453,7 +2453,7 @@ cpp_dec_float::cpp_dec_float(const double man d -= static_cast(n); d *= static_cast(cpp_dec_float_elem_mask); } -} // LCOV_EXCL_LINE +} // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. template template @@ -3158,7 +3158,7 @@ cpp_dec_float cpp_dec_float::two(), static_cast(p), std::integral_constant()); return t; -} // LCOV_EXCL_LINE +} // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. template inline void eval_add(cpp_dec_float& result, const cpp_dec_float& o) @@ -3443,7 +3443,7 @@ inline void eval_ldexp(cpp_dec_float& result, if (2 * half_exp != e) t *= 2; result *= t; - } // LCOV_EXCL_LINE + } // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. else result *= local_cpp_dec_float_type::pow2(e); } From 4f4783636142e9872e871d598fd321f1ea500db5 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 6 Jul 2025 14:07:23 +0200 Subject: [PATCH 013/114] Annotate and slight rework some tests for cover --- test/git_issue_167.cpp | 33 +++++++++++++++++------ test/test_atan.cpp | 45 +++++++++++++++++++------------- test/test_cpp_bin_float_conv.cpp | 25 +++++++++++++----- test/test_cpp_int.cpp | 13 ++++++--- test/test_float_io.cpp | 6 +++++ 5 files changed, 85 insertions(+), 37 deletions(-) diff --git a/test/git_issue_167.cpp b/test/git_issue_167.cpp index ddfc91fac..9b0c52716 100644 --- a/test/git_issue_167.cpp +++ b/test/git_issue_167.cpp @@ -1,23 +1,40 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright 2019 John Maddock. Distributed under the Boost +// Copyright 2019 - 2025 John Maddock. +// Copyright 2025 Christopher Kormanyos. +// 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) +#include + #include -#include "test.hpp" + +#include +#include int main() { - try{ + std::string str { }; + + try + { + using local_dec_float_type = boost::multiprecision::cpp_dec_float_50; + std::locale::global(std::locale("en-US")); - boost::multiprecision::cpp_dec_float_50 d("1234.56"); - std::string s = d.str(); + local_dec_float_type d("1234.56"); + str = d.str(); + + BOOST_CHECK_EQUAL(str, "1234.56"); - BOOST_CHECK_EQUAL(s, "1234.56"); + d = local_dec_float_type("123.456"); + str = d.str(); + + BOOST_CHECK_EQUAL(str, "123.456"); } catch(const std::runtime_error&){} // No en-US locale + BOOST_CHECK(!str.empty()); + BOOST_CHECK(str.find('.') != std::string::npos); + return boost::report_errors(); } - - diff --git a/test/test_atan.cpp b/test/test_atan.cpp index 8c9ab2d87..893061eb6 100644 --- a/test/test_atan.cpp +++ b/test/test_atan.cpp @@ -13,9 +13,11 @@ #define _SCL_SECURE_NO_WARNINGS #endif +#include + #include + #include -#include "test.hpp" #if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT) && !defined(TEST_CPP_DOUBLE_FLOAT) #define TEST_MPF_50 @@ -73,11 +75,16 @@ T atan2_def(T y, T x) T t; t.backend() = boost::multiprecision::default_ops::get_constant_pi(); T t2; - if (x) + if (x != 0) + { t2 = atan(y / x); + t2 += T((t / 2) * (1 - x.sign()) * T(y.sign() + 0.5).sign()); + } else + { t2 = y.sign() * t / 2; - return t2 + (t / 2) * (1 - x.sign()) * T(y.sign() + 0.5).sign(); + } + return t2; } template @@ -158,13 +165,13 @@ void test() T e = relative_error(val, T(data[k])); unsigned err = e.template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. val = atan(-arg); e = relative_error(val, T(-T(data[k]))); err = e.template convert_to(); if (err > max_err) { - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } arg *= 10000; } @@ -184,28 +191,28 @@ void test() err = e.template convert_to(); if (err > max_err) { - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } val = atan2(-arg, 1); e = relative_error(val, atan2_def(T(-arg), T(1))); err = e.template convert_to(); if (err > max_err) { - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } val = atan2(arg, -1); e = relative_error(val, atan2_def(arg, T(-1))); err = e.template convert_to(); if (err > max_err) { - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } val = atan2(-arg, -1); e = relative_error(val, atan2_def(T(-arg), T(-1))); err = e.template convert_to(); if (err > max_err) { - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } arg *= 10000; } @@ -214,13 +221,13 @@ void test() // err = relative_error(T(atan2(T(0), T(1))), atan2_def(T(0), T(1))).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. if (!boost::multiprecision::is_interval_number::value) { // We don't test this with intervals as [-0,0] leads to strange behaviour in atan2... err = relative_error(T(atan2(T(0), T(-1))), atan2_def(T(0), T(-1))).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } T pi; @@ -228,29 +235,31 @@ void test() err = relative_error(T(atan2(T(1), T(0))), T(pi / 2)).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. err = relative_error(T(atan2(T(-1), T(0))), T(pi / -2)).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. T mv = (std::numeric_limits::max)(); - err = relative_error(T(atan2(mv, T(1))), T(pi / 2)).template convert_to(); + err = relative_error(T(atan2(mv, T(1))), T(pi / 2)).template convert_to(); + err = relative_error(T(atan2(mv, T(1))), atan2_def(T(mv), T(1))).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. err = relative_error(T(atan2(-mv, T(1))), T(pi / -2)).template convert_to(); + err = relative_error(T(atan2(-mv, T(1))), atan2_def(T(-mv), T(0))).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. if (std::numeric_limits::has_infinity) { mv = (std::numeric_limits::infinity)(); err = relative_error(T(atan2(mv, T(1))), T(pi / 2)).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. err = relative_error(T(atan2(-mv, T(1))), T(pi / -2)).template convert_to(); if (err > max_err) - max_err = err; + max_err = err; // LCOV_EXCL_LINE This line might not necessarily be expected to get hit in tests. } std::cout << "Max error was: " << max_err << std::endl; diff --git a/test/test_cpp_bin_float_conv.cpp b/test/test_cpp_bin_float_conv.cpp index 70412e743..9f26a7061 100644 --- a/test/test_cpp_bin_float_conv.cpp +++ b/test/test_cpp_bin_float_conv.cpp @@ -1,5 +1,7 @@ /////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost +// Copyright 2012 - 2025 John Maddock. +// Copyright 2025 Christopher Kormanyos. +// Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt // @@ -8,14 +10,15 @@ #define _SCL_SECURE_NO_WARNINGS #endif +#include + #include -#include -#include "test.hpp" -#include #include #include #include + +#include #include template @@ -48,6 +51,7 @@ void check_round(const T& val, bool check_extended = false) if (diff2 < diff1) { // Some debugging code here... + // LCOV_EXCL_START These lines are not expected to get hit in tests. std::cout << val.str() << std::endl; std::cout << std::setprecision(18); std::cout << d1 << std::endl; @@ -55,14 +59,19 @@ void check_round(const T& val, bool check_extended = false) std::cout << diff1 << std::endl; std::cout << diff2 << std::endl; d1 = val.template convert_to(); + // LCOV_EXCL_STOP These lines are not expected to get hit in tests. } + + using std::signbit; + BOOST_CHECK(diff2 >= diff1); - BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(d1)); + // Note: Ask John if boost::multiprecision::signbit() should return bool? + BOOST_CHECK_EQUAL((boost::math::signbit(val) != 0), signbit(d1)); float f1 = val.template convert_to(); float f2 = boost::math::nextafter(f1, f1 < val ? FLT_MAX : -FLT_MAX); BOOST_CHECK(((abs(f1 - val) <= abs(f2 - val)))); - BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(f1)); + BOOST_CHECK_EQUAL((boost::math::signbit(val) != 0), signbit(f1)); #if !defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) @@ -78,6 +87,7 @@ void check_round(const T& val, bool check_extended = false) if (diff2 < diff1) { // Some debugging code here... + // LCOV_EXCL_START These lines are not expected to get hit in tests. std::cout << val.str() << std::endl; std::cout << std::setprecision(18); std::cout << l1 << std::endl; @@ -85,9 +95,10 @@ void check_round(const T& val, bool check_extended = false) std::cout << diff1 << std::endl; std::cout << diff2 << std::endl; l1 = val.template convert_to(); + // LCOV_EXCL_STOP These lines are not expected to get hit in tests. } BOOST_CHECK(diff2 >= diff1); - BOOST_CHECK_EQUAL(boost::math::signbit(val), boost::math::signbit(l1)); + BOOST_CHECK_EQUAL((boost::math::signbit(val) != 0), signbit(l1)); } #endif diff --git a/test/test_cpp_int.cpp b/test/test_cpp_int.cpp index d28b7a1b2..194c9e2c5 100644 --- a/test/test_cpp_int.cpp +++ b/test/test_cpp_int.cpp @@ -1,5 +1,7 @@ /////////////////////////////////////////////////////////////// -// Copyright 2012 John Maddock. Distributed under the Boost +// Copyright 2012 - 2025 John Maddock. +// Copyright 2025 Christopher Kormanyos. +// Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt @@ -17,12 +19,13 @@ // #define BOOST_MP_USE_LIMB_SHIFT -#include +#include +#include + #include +#include #include #include -#include "timer.hpp" -#include "test.hpp" #ifdef _MSC_VER #pragma warning(disable : 4127) // Conditional expression is constant @@ -785,6 +788,7 @@ struct tester if (last_error_count != (unsigned)boost::detail::test_errors()) { + // LCOV_EXCL_START These lines are not expected to get hit in tests. last_error_count = boost::detail::test_errors(); std::cout << std::hex << std::showbase; @@ -817,6 +821,7 @@ struct tester std::cout << "a1%b1 = " << a1 % b1 << std::endl; std::cout << "a%d = " << a % d << std::endl; std::cout << "a1%d1 = " << a1 % d1 << std::endl; + // LCOV_EXCL_STOP These lines are not expected to get hit in tests. } // diff --git a/test/test_float_io.cpp b/test/test_float_io.cpp index 04d0ca469..b0621ce32 100644 --- a/test/test_float_io.cpp +++ b/test/test_float_io.cpp @@ -109,6 +109,7 @@ bool is_bankers_rounding_error(const std::string& s, const char* expect) return true; } +// LCOV_EXCL_START These lines are not expected to get hit in tests. void print_flags(std::ios_base::fmtflags f) { std::cout << "Formatting flags were: "; @@ -122,6 +123,7 @@ void print_flags(std::ios_base::fmtflags f) std::cout << "showpos "; std::cout << std::endl; } +// LCOV_EXCL_STOP These lines are not expected to get hit in tests. template void test() @@ -162,6 +164,7 @@ void test() } else { + // LCOV_EXCL_START These lines are not expected to get hit in tests. std::cout << std::setprecision(20) << "Testing value " << val << std::endl; print_flags(f[i]); std::cout << "Precision is: " << prec << std::endl; @@ -169,6 +172,7 @@ void test() std::cout << "Expected: " << expect << std::endl; ++boost::detail::test_errors(); mp_t(val).str(prec, f[i]); // for debugging + // LCOV_EXCL_STOP These lines are not expected to get hit in tests. } } } @@ -195,6 +199,7 @@ void test() const char* expect = zeros[col]; if (ss.str() != expect) { + // LCOV_EXCL_START These lines are not expected to get hit in tests. std::cout << std::setprecision(20) << "Testing value " << val << std::endl; print_flags(f[i]); std::cout << "Precision is: " << prec << std::endl; @@ -202,6 +207,7 @@ void test() std::cout << "Expected: " << expect << std::endl; ++boost::detail::test_errors(); mp_t(val).str(prec, f[i]); // for debugging + // LCOV_EXCL_STOP These lines are not expected to get hit in tests. } } } From 14f4212c02121abc7706e3ec9bff64cf26448266 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 6 Jul 2025 19:00:22 +0200 Subject: [PATCH 014/114] Repair and cover test for issue167 --- test/git_issue_167.cpp | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/test/git_issue_167.cpp b/test/git_issue_167.cpp index 9b0c52716..82adfc75b 100644 --- a/test/git_issue_167.cpp +++ b/test/git_issue_167.cpp @@ -20,21 +20,39 @@ int main() { using local_dec_float_type = boost::multiprecision::cpp_dec_float_50; - std::locale::global(std::locale("en-US")); - local_dec_float_type d("1234.56"); - str = d.str(); + // Test a string with a decimal point. + local_dec_float_type d_first("1234.56"); + str = d_first.str(); + BOOST_CHECK(str == "1234.56"); - BOOST_CHECK_EQUAL(str, "1234.56"); + // Set a new local locale. + #if (defined(__GNUC__) && defined(__x86_64__)) + // We really want to pick up these lines on GHA coverage runners. + const char* p_str_loc = std::setlocale(LC_ALL, "C.UTF-8"); + #else + const char* p_str_loc = std::setlocale(LC_ALL, "en_US.UTF-8"); + #endif - d = local_dec_float_type("123.456"); - str = d.str(); + std::cout << "A local locale was set: " << p_str_loc << std::endl; - BOOST_CHECK_EQUAL(str, "123.456"); + { + std::stringstream strm { }; + + strm << p_str_loc; + + // Check the new local locale. + BOOST_CHECK(strm.str().find("UTF") != std::string::npos); + } + + // Test another string with a decimal point. + local_dec_float_type d_other("123.789"); + str = d_other.str(); + + BOOST_CHECK(str == "123.789"); } - catch(const std::runtime_error&){} // No en-US locale + catch(const std::runtime_error&) { } // LCOV_EXCL_LINE No locale was found. - BOOST_CHECK(!str.empty()); - BOOST_CHECK(str.find('.') != std::string::npos); + BOOST_CHECK(str == "123.789"); return boost::report_errors(); } From 4f1bae2869f1e40acad36cf9889ae406ecd88b26 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 6 Jul 2025 21:31:37 +0200 Subject: [PATCH 015/114] Continue patching issue167 and get cover --- test/git_issue_167.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/git_issue_167.cpp b/test/git_issue_167.cpp index 82adfc75b..ec53141ed 100644 --- a/test/git_issue_167.cpp +++ b/test/git_issue_167.cpp @@ -16,6 +16,9 @@ int main() { std::string str { }; + // Test strings with a decimal point. Keep in the spirit of the + // original test case. But don't simply try-catch out of it. + try { using local_dec_float_type = boost::multiprecision::cpp_dec_float_50; @@ -25,13 +28,14 @@ int main() str = d_first.str(); BOOST_CHECK(str == "1234.56"); - // Set a new local locale. - #if (defined(__GNUC__) && defined(__x86_64__)) - // We really want to pick up these lines on GHA coverage runners. + #if (defined(__GNUC__) && defined(__x86_64__) && !defined(WIN32) && !defined(__APPLE__)) + + // Set a new local locale. All these compilers have different + // locale names. In this particular case, we stay with the + // original spirit of the test in issue167 at least on Ubuntu + // GHA runners. We also wanto to pick up these lines of coverage. + const char* p_str_loc = std::setlocale(LC_ALL, "C.UTF-8"); - #else - const char* p_str_loc = std::setlocale(LC_ALL, "en_US.UTF-8"); - #endif std::cout << "A local locale was set: " << p_str_loc << std::endl; @@ -43,8 +47,8 @@ int main() // Check the new local locale. BOOST_CHECK(strm.str().find("UTF") != std::string::npos); } + #endif - // Test another string with a decimal point. local_dec_float_type d_other("123.789"); str = d_other.str(); From 912326798e1de7b6b9b74743a187faeda76401a7 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 6 Jul 2025 22:55:16 +0200 Subject: [PATCH 016/114] Exclude set-locale on __MINGW__ runners --- test/git_issue_167.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/git_issue_167.cpp b/test/git_issue_167.cpp index ec53141ed..469f4d35f 100644 --- a/test/git_issue_167.cpp +++ b/test/git_issue_167.cpp @@ -28,7 +28,10 @@ int main() str = d_first.str(); BOOST_CHECK(str == "1234.56"); - #if (defined(__GNUC__) && defined(__x86_64__) && !defined(WIN32) && !defined(__APPLE__)) + #if (defined(__GNUC__) && defined(__x86_64__) && !(defined(_WIN32) || defined(__MINGW32__)) && !defined(__APPLE__)) + + // That preprocessor line is detailed, but it really essentially is + // just trying to isolate Ubuntu runners on GHA for test and coverage. // Set a new local locale. All these compilers have different // locale names. In this particular case, we stay with the From d9fe99069ace430391ff43595fd91e65a45da02c Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 7 Jul 2025 10:38:16 +0200 Subject: [PATCH 017/114] Improve and run tests issue464 --- test/Jamfile.v2 | 1 + test/git_issue_464.cpp | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 8bb53c7c0..71463828a 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1257,6 +1257,7 @@ test-suite misc : [ run git_issue_426.cpp : : : [ check-target-builds ../config//has_mpfr : gmp mpfr TEST_MPFR ] [ check-target-builds ../config//has_float128 : quadmath TEST_FLOAT128 ] ] [ run git_issue_277.cpp ] [ run git_issue_313.cpp ] + [ run git_issue_464.cpp ] [ run git_issue_488.cpp ] [ run git_issue_506.cpp ] [ run git_issue_509.cpp : : : [ requires cpp_lib_bit_cast ] ] diff --git a/test/git_issue_464.cpp b/test/git_issue_464.cpp index dc9d0054a..efe082074 100644 --- a/test/git_issue_464.cpp +++ b/test/git_issue_464.cpp @@ -1,24 +1,53 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright 2022 Matt Borland. Distributed under the Boost +// Copyright 2022 - 2025 Matt Borland. +// Copyright 2025 Christopher Kormanyos. +// 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) // // See: https://github.com/boostorg/multiprecision/issues/464 +#include + #include -#include -#include "test.hpp" + +#include +#include template void test() { - auto a = boost::multiprecision::cpp_dec_float_50 {12345}; + auto a = boost::multiprecision::cpp_dec_float_50 { 12345 }; auto d1 = a.convert_to(); + { + std::stringstream strm { }; + strm.imbue(std::locale::classic()); + strm << std::scientific << std::setprecision(8) << d1; + + BOOST_CHECK(strm.str().find("1.2345") != std::string::npos); + } + + #if defined(BOOST_MSVC) + // Ubuntu runners did not have this locale in GCC. + // Windows runners seem like they do have this locale in MSVC. + // I did not check any others + + // Set a different locale. std::locale::global(std::locale("de_DE")); + #endif + auto d2 = a.convert_to(); + { + std::stringstream strm { }; + strm.imbue(std::locale::classic()); + strm << std::scientific << std::setprecision(8) << d2; + + BOOST_CHECK(strm.str().find("1.2345") != std::string::npos); + } + BOOST_CHECK_EQUAL(d1, d2); } From cef08fae9470619cbd77b93e9fdeefa6d3df7a0a Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 7 Jul 2025 16:40:20 +0200 Subject: [PATCH 018/114] More tests and repair edge case pow --- .../boost/multiprecision/cpp_double_fp.hpp | 49 ++++++++++++++----- test/test_sf_import_c99.cpp | 29 ++++++++--- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 22b79b671..c5924dfc7 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -1479,10 +1479,6 @@ constexpr auto eval_pow(cpp_double_fp_backend& result, const result = one; } - else if (fpc_a == FP_NAN) - { - result = double_float_type::my_value_nan(); - } else if (fpc_x == FP_ZERO) { if ((fpc_a == FP_NORMAL) || (fpc_a == FP_INFINITE)) @@ -1495,6 +1491,10 @@ constexpr auto eval_pow(cpp_double_fp_backend& result, const result = (eval_signbit(a) ? double_float_type::my_value_inf() : zero); } + else if (fpc_a == FP_NAN) + { + result = double_float_type::my_value_nan(); + } } else if (fpc_x == FP_INFINITE) { @@ -1505,6 +1505,10 @@ constexpr auto eval_pow(cpp_double_fp_backend& result, const result = (eval_signbit(a) ? zero : double_float_type::my_value_inf()); } + else if (fpc_a == FP_NAN) + { + result = double_float_type::my_value_nan(); + } } else if (fpc_x != FP_NORMAL) { @@ -1512,14 +1516,37 @@ constexpr auto eval_pow(cpp_double_fp_backend& result, const } else { - if (fpc_a == FP_INFINITE) + if (fpc_a == FP_ZERO) { - result = - ( - (x.compare(one) == -1) ? (eval_signbit(a) ? double_float_type::my_value_inf() : zero) - : (x.compare(one) == +1) ? (eval_signbit(a) ? zero : double_float_type::my_value_inf()) - : one - ); + result = one; + } + else if (fpc_a == FP_INFINITE) + { + constexpr double_float_type one_minus { -1 }; + + if (x.compare(one_minus) == 0) + { + result = one; + } + else + { + double_float_type xabs { }; + + eval_fabs(xabs, x); + + const int compare_one_result { xabs.compare(one) }; + + result = + ( + (compare_one_result < 0) ? (eval_signbit(a) ? double_float_type::my_value_inf() : zero) + : (compare_one_result > 0) ? (eval_signbit(a) ? zero : double_float_type::my_value_inf()) + : one + ); + } + } + else if (fpc_a == FP_NAN) + { + result = (x.compare(one) == 0) ? one : double_float_type::my_value_nan(); } else { diff --git a/test/test_sf_import_c99.cpp b/test/test_sf_import_c99.cpp index 457c87c45..a9e44dced 100644 --- a/test/test_sf_import_c99.cpp +++ b/test/test_sf_import_c99.cpp @@ -638,51 +638,59 @@ void test_poison() } template -bool type_sets_errno(const T&) +static bool type_sets_errno(const T&) { return true; } #ifdef TEST_MPFR_50 template -bool type_sets_errno(const boost::multiprecision::number, ExpressionTemplates>&) +static bool type_sets_errno(const boost::multiprecision::number, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFR_DEBUG_ADAPTOR template -bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFI_DEBUG_ADAPTOR template -bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFR_LOGGED_ADAPTOR template -bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFI_LOGGED_ADAPTOR template -bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_FLOAT128 -bool type_sets_errno(const boost::multiprecision::float128&) +static bool type_sets_errno(const boost::multiprecision::float128&) { return false; } #endif +#ifdef TEST_CPP_DOUBLE_FLOAT +static bool type_sets_errno(const boost::multiprecision::cpp_double_float&) { return false; } +static bool type_sets_errno(const boost::multiprecision::cpp_double_double&) { return false; } +static bool type_sets_errno(const boost::multiprecision::cpp_double_long_double&) { return false; } +#if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128) +static bool type_sets_errno(const boost::multiprecision::cpp_double_float128&) { return false; } +#endif +#endif template typename std::enable_if::is_specialized>::type check_invalid(const T& val) @@ -2263,6 +2271,7 @@ int main() #endif #endif #ifdef TEST_CPP_BIN_FLOAT + test_c99_appendix_F(); test(); test, boost::multiprecision::et_on> >(); #endif @@ -2297,8 +2306,14 @@ int main() #ifdef TEST_CPP_DOUBLE_FLOAT test(); test(); + + test_c99_appendix_F(); + test_c99_appendix_F(); + #if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128) test(); + + test_c99_appendix_F(); #endif #endif From 600568762c30623b3c8f2f9a0f6e14092da70344 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 7 Jul 2025 18:33:06 +0200 Subject: [PATCH 019/114] Revert test needing slight backend repairs --- test/test_sf_import_c99.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/test_sf_import_c99.cpp b/test/test_sf_import_c99.cpp index a9e44dced..83f9a23ea 100644 --- a/test/test_sf_import_c99.cpp +++ b/test/test_sf_import_c99.cpp @@ -2271,7 +2271,6 @@ int main() #endif #endif #ifdef TEST_CPP_BIN_FLOAT - test_c99_appendix_F(); test(); test, boost::multiprecision::et_on> >(); #endif From f5d4bbf8153ef28b7de95096b4885dfc5f4978f5 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 7 Jul 2025 19:54:22 +0200 Subject: [PATCH 020/114] Minor corrections selected C99 edge cases --- .../multiprecision/detail/default_ops.hpp | 38 +++++++++++++++++-- test/test_sf_import_c99.cpp | 31 ++++++++------- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index 0e8debe1e..5a17e78f6 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -2619,6 +2619,15 @@ inline BOOST_MP_CXX14_CONSTEXPR typename multiprecision::detail::expression inline BOOST_MP_CXX14_CONSTEXPR multiprecision::number erf BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) { + using local_number_type = multiprecision::number; + + const int fpc_arg { (boost::multiprecision::fpclassify)(arg) }; + + if (fpc_arg == FP_ZERO) + { + return arg; + } + detail::scoped_default_precision > precision_guard(arg); return boost::math::erf(arg, c99_error_policy()); } @@ -2626,7 +2635,15 @@ template inline BOOST_MP_CXX14_CONSTEXPR typename multiprecision::detail::expression::result_type erf BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::detail::expression& arg) { using value_type = typename multiprecision::detail::expression::result_type; - detail::scoped_default_precision precision_guard(arg); + detail::scoped_default_precision precision_guard(arg); + + const int fpc_arg { (boost::multiprecision::fpclassify)(arg) }; + + if (fpc_arg == FP_ZERO) + { + return arg; + } + return erf(value_type(arg)); } template @@ -2678,12 +2695,27 @@ inline BOOST_MP_CXX14_CONSTEXPR typename multiprecision::detail::expression inline BOOST_MP_CXX14_CONSTEXPR multiprecision::number tgamma BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) { - detail::scoped_default_precision > precision_guard(arg); - if ((arg == 0) && std::numeric_limits >::has_infinity) + using local_number_type = multiprecision::number; + + detail::scoped_default_precision precision_guard(arg); + + const int fpc_arg { (boost::multiprecision::fpclassify)(arg) }; + + if ((fpc_arg == FP_ZERO) && std::numeric_limits::has_infinity) { errno = ERANGE; return 1 / arg; } + else if ((fpc_arg == FP_NAN) && std::numeric_limits::has_quiet_NaN) + { + return arg; + } + else if ((fpc_arg == FP_INFINITE) && std::numeric_limits::has_infinity && std::numeric_limits::has_quiet_NaN) + { + const bool is_neg { arg.compare(local_number_type(0)) < 0 }; + + return ((!is_neg) ? std::numeric_limits::infinity() : std::numeric_limits::quiet_NaN()); + } return boost::math::tgamma(arg, c99_error_policy()); } template diff --git a/test/test_sf_import_c99.cpp b/test/test_sf_import_c99.cpp index 83f9a23ea..b085f9191 100644 --- a/test/test_sf_import_c99.cpp +++ b/test/test_sf_import_c99.cpp @@ -638,57 +638,57 @@ void test_poison() } template -static bool type_sets_errno(const T&) +static constexpr bool type_sets_errno(const T&) { return true; } #ifdef TEST_MPFR_50 template -static bool type_sets_errno(const boost::multiprecision::number, ExpressionTemplates>&) +static constexpr bool type_sets_errno(const boost::multiprecision::number, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFR_DEBUG_ADAPTOR template -static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static constexpr bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFI_DEBUG_ADAPTOR template -static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static constexpr bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFR_LOGGED_ADAPTOR template -static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static constexpr bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_MPFI_LOGGED_ADAPTOR template -static bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) +static constexpr bool type_sets_errno(const boost::multiprecision::number >, ExpressionTemplates>&) { return false; } #endif #ifdef TEST_FLOAT128 -static bool type_sets_errno(const boost::multiprecision::float128&) +static constexpr bool type_sets_errno(const boost::multiprecision::float128&) { return false; } #endif #ifdef TEST_CPP_DOUBLE_FLOAT -static bool type_sets_errno(const boost::multiprecision::cpp_double_float&) { return false; } -static bool type_sets_errno(const boost::multiprecision::cpp_double_double&) { return false; } -static bool type_sets_errno(const boost::multiprecision::cpp_double_long_double&) { return false; } +static constexpr bool type_sets_errno(const boost::multiprecision::cpp_double_float&) { return false; } +static constexpr bool type_sets_errno(const boost::multiprecision::cpp_double_double&) { return false; } +static constexpr bool type_sets_errno(const boost::multiprecision::cpp_double_long_double&) { return false; } #if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128) -static bool type_sets_errno(const boost::multiprecision::cpp_double_float128&) { return false; } +static constexpr bool type_sets_errno(const boost::multiprecision::cpp_double_float128&) { return false; } #endif #endif @@ -704,7 +704,9 @@ typename std::enable_if::is_specialized>::type check_inva BOOST_CHECK_EQUAL(val, 0); } if (type_sets_errno(val)) + { BOOST_CHECK_EQUAL(errno, EDOM); + } errno = 0; } @@ -1913,15 +1915,15 @@ void test_c99_appendix_F() arg = -1; check_invalid(tgamma(arg)); arg = -std::numeric_limits::infinity(); - check_invalid(tgamma(arg)); + BOOST_CHECK((boost::multiprecision::isnan)(tgamma(arg))); arg = std::numeric_limits::infinity(); val = tgamma(arg); - BOOST_CHECK_EQUAL(val, std::numeric_limits::infinity()); + BOOST_CHECK((boost::multiprecision::isinf)(val)); } if (std::numeric_limits::has_quiet_NaN) { arg = std::numeric_limits::quiet_NaN(); - check_invalid(tgamma(arg)); + BOOST_CHECK((boost::multiprecision::isnan)(tgamma(arg))); } // F.9.6.1: arg = 0; @@ -2271,6 +2273,7 @@ int main() #endif #endif #ifdef TEST_CPP_BIN_FLOAT + test_c99_appendix_F(); test(); test, boost::multiprecision::et_on> >(); #endif From 7371ebf77d6ba55ab64f04362ae15646868eaf7f Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 7 Jul 2025 20:48:23 +0200 Subject: [PATCH 021/114] Patch tests for double-fp --- .../multiprecision/detail/default_ops.hpp | 2 -- test/test_sf_import_c99.cpp | 22 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index 5a17e78f6..6fd4b65eb 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -2619,8 +2619,6 @@ inline BOOST_MP_CXX14_CONSTEXPR typename multiprecision::detail::expression inline BOOST_MP_CXX14_CONSTEXPR multiprecision::number erf BOOST_PREVENT_MACRO_SUBSTITUTION(const multiprecision::number& arg) { - using local_number_type = multiprecision::number; - const int fpc_arg { (boost::multiprecision::fpclassify)(arg) }; if (fpc_arg == FP_ZERO) diff --git a/test/test_sf_import_c99.cpp b/test/test_sf_import_c99.cpp index b085f9191..d8bb0ff8d 100644 --- a/test/test_sf_import_c99.cpp +++ b/test/test_sf_import_c99.cpp @@ -802,10 +802,10 @@ void test_c99_appendix_F() { arg = std::numeric_limits::infinity(); val = atan(arg); - BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg = -std::numeric_limits::infinity(); val = atan(arg); - BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, -boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } } if (std::numeric_limits::has_quiet_NaN) { @@ -842,7 +842,7 @@ void test_c99_appendix_F() arg = 0; arg2 = -2; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, boost::math::constants::pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::pi(), std::numeric_limits::epsilon() * 8); } arg = -arg; if (signbit(arg)) { @@ -864,7 +864,7 @@ void test_c99_appendix_F() arg = -2; arg2 = 0; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, -boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg2 = -arg2; if (signbit(arg2)) { @@ -874,7 +874,7 @@ void test_c99_appendix_F() arg = 2; arg2 = 0; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg2 = -arg2; if (signbit(arg2)) { @@ -886,10 +886,10 @@ void test_c99_appendix_F() arg = 2; arg2 = -std::numeric_limits::infinity(); val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, boost::math::constants::pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::pi(), std::numeric_limits::epsilon() * 8); } arg = -arg; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, -boost::math::constants::pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, -boost::math::constants::pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, -boost::math::constants::pi(), std::numeric_limits::epsilon() * 8); } arg = 2; arg2 = std::numeric_limits::infinity(); val = atan2(arg, arg2); @@ -905,17 +905,17 @@ void test_c99_appendix_F() arg = std::numeric_limits::infinity(); arg2 = 2; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg = -arg; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, -boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg = std::numeric_limits::infinity(); arg2 = -2; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg = -arg; val = atan2(arg, arg2); - BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()); + BOOST_IF_CONSTEXPR (!has_poor_exp_range_or_precision_support::value) { BOOST_CHECK_EQUAL(val, -boost::math::constants::half_pi()) } else { BOOST_CHECK_CLOSE_FRACTION(val, -boost::math::constants::half_pi(), std::numeric_limits::epsilon() * 8); } arg = std::numeric_limits::infinity(); arg2 = -std::numeric_limits::infinity(); val = atan2(arg, arg2); From 48ec510f914b9fdd5d74d387b618b2c48df4a473 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 8 Jul 2025 19:36:56 +0200 Subject: [PATCH 022/114] Slightly refactor a private constructor --- .../boost/multiprecision/cpp_dec_float.hpp | 80 +++++++++---------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/include/boost/multiprecision/cpp_dec_float.hpp b/include/boost/multiprecision/cpp_dec_float.hpp index 382d741d6..e5e6ee940 100644 --- a/include/boost/multiprecision/cpp_dec_float.hpp +++ b/include/boost/multiprecision/cpp_dec_float.hpp @@ -222,7 +222,7 @@ class cpp_dec_float // LCOV_EXCL_LINE This causes a false negative on lcov cover *this = val; } - cpp_dec_float(const double mantissa, const exponent_type exponent); + cpp_dec_float(const double mantissa, const exponent_type exp10); std::size_t hash() const { @@ -2391,10 +2391,13 @@ bool cpp_dec_float::rd_string(const char* con } template -cpp_dec_float::cpp_dec_float(const double mantissa, const ExponentType exponent) +cpp_dec_float::cpp_dec_float(const double mantissa, const ExponentType exp10) { - // Create *this cpp_dec_float from a given mantissa and exponent. - // Note: This constructor does not maintain the full precision of double. + // Create *this cpp_dec_float + // from a given mantissa and base-10 exponent. It is important + // to note that this constructor does *not* maintain the full + // precision of double. It is used internally (privately), mostly + // as a first guess for Newton iteration in square root and inversion. std::fill(data.begin(), data.end(), static_cast(0u)); @@ -2407,51 +2410,48 @@ cpp_dec_float::cpp_dec_float(const double man ) }; - using std::fabs; - - const bool mantissa_is_iszero = (fabs(mantissa) < dbl_min_check); - - if (mantissa_is_iszero) - { - return; - } - const bool b_neg { (mantissa < 0.0) }; - double d { ((!b_neg) ? mantissa : -mantissa) }; - exponent_type e { exponent }; + double d_mant { ((!b_neg) ? mantissa : -mantissa) }; - while (d > 10.0) - { - d /= 10.0; - ++e; - } - while (d < 1.0) + const bool mantissa_is_non_zero { (d_mant > dbl_min_check) }; + + if (mantissa_is_non_zero) { - d *= 10.0; - --e; - } + exponent_type exp10_val { exp10 }; + + while (d_mant > 10.0) + { + d_mant /= 10.0; + ++exp10_val; + } + while (d_mant < 1.0) + { + d_mant *= 10.0; + --exp10_val; + } - std::int32_t shift = static_cast(e % static_cast(cpp_dec_float_elem_digits10)); + std::int32_t shift = static_cast(exp10_val % static_cast(cpp_dec_float_elem_digits10)); - while (static_cast(shift-- % cpp_dec_float_elem_digits10) != static_cast(0)) - { - d *= 10.0; - --e; - } + while (static_cast(shift-- % cpp_dec_float_elem_digits10) != static_cast(0)) + { + d_mant *= 10.0; + --exp10_val; + } - exp = e; - neg = b_neg; + exp = exp10_val; + neg = b_neg; - constexpr std::int32_t digit_ratio = static_cast(static_cast(std::numeric_limits::digits10) / static_cast(cpp_dec_float_elem_digits10)); - constexpr std::int32_t digit_loops = static_cast(digit_ratio + static_cast(2)); + constexpr std::int32_t digit_ratio = static_cast(static_cast(std::numeric_limits::digits10) / static_cast(cpp_dec_float_elem_digits10)); + constexpr std::int32_t digit_loops = static_cast(digit_ratio + static_cast(2)); - for (std::int32_t i = static_cast(0); i < digit_loops; i++) - { - std::uint32_t n = static_cast(static_cast(d)); - data[static_cast(i)] = static_cast(n); - d -= static_cast(n); - d *= static_cast(cpp_dec_float_elem_mask); + for (std::int32_t i = static_cast(0); i < digit_loops; i++) + { + std::uint32_t n = static_cast(static_cast(d_mant)); + data[static_cast(i)] = static_cast(n); + d_mant -= static_cast(n); + d_mant *= static_cast(cpp_dec_float_elem_mask); + } } } // LCOV_EXCL_LINE This causes a false negative on lcov coverage test. From d2a292dd5c3c831894c4be1e9b40e66555e8f2a6 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 10 Jul 2025 10:01:13 -0600 Subject: [PATCH 023/114] Correct reference to GMP 5.1 mpfr_class wrapper Fixes #712 --- doc/introduction.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/introduction.qbk b/doc/introduction.qbk index 4eb3b5500..b7e68b400 100644 --- a/doc/introduction.qbk +++ b/doc/introduction.qbk @@ -208,7 +208,7 @@ if we were using the [mpfr_class] C++ wrapper for [mpfr] - then this expression temporaries (this is true even though [mpfr_class] does use expression templates to reduce the number of temporaries somewhat). Had we used an even simpler wrapper around [mpfr] like [mpreal] things would have been even worse and no less that 24 temporaries are created for this simple expression (note - we actually measure the number of memory allocations performed rather than -the number of temporaries directly, note also that the [mpf_class] wrapper that will be supplied with GMP-5.1 reduces the number of +the number of temporaries directly, note also that the [mpf_class] wrapper supplied with GMP-5.1 or later reduces the number of temporaries to pretty much zero). Note that if we compile with expression templates disabled and rvalue-reference support on, then actually still have no wasted memory allocations as even though temporaries are created, their contents are moved rather than copied. From d31b9b0e6cff5e5c6680a4ef4ef0069d20b881c0 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 10 Jul 2025 10:06:01 -0600 Subject: [PATCH 024/114] Correct markup instructions Fixes #715 --- doc/html/boost_multiprecision/tut/interval.html | 2 +- doc/tutorial_interval_mpfi.qbk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/html/boost_multiprecision/tut/interval.html b/doc/html/boost_multiprecision/tut/interval.html index e9dfce501..5ec80339d 100644 --- a/doc/html/boost_multiprecision/tut/interval.html +++ b/doc/html/boost_multiprecision/tut/interval.html @@ -32,7 +32,7 @@ There is one currently only one interval number type supported - MPFI.

- [ section:mpfi mpfi_float] + [section:mpfi mpfi_float]