diff --git a/include/boost/decimal/detail/cmath/cosh.hpp b/include/boost/decimal/detail/cmath/cosh.hpp index a48e912b5..6e1e306b3 100644 --- a/include/boost/decimal/detail/cmath/cosh.hpp +++ b/include/boost/decimal/detail/cmath/cosh.hpp @@ -67,13 +67,17 @@ constexpr auto cosh_impl(const T x) noexcept result = fma(result, xsq, one); } - else + else if (x > one) { const auto exp_pos_val = exp(x); - constexpr T two { 2, 0 }; + result = (exp_pos_val + (one / exp_pos_val)) / 2; + } + else + { + constexpr T local_cosh_one { numbers::e_v }; - result = (exp_pos_val + (one / exp_pos_val)) / two; + result = (local_cosh_one + (one / local_cosh_one)) / 2; } } } diff --git a/include/boost/decimal/detail/cmath/exp.hpp b/include/boost/decimal/detail/cmath/exp.hpp index 34429ebe6..364957603 100644 --- a/include/boost/decimal/detail/cmath/exp.hpp +++ b/include/boost/decimal/detail/cmath/exp.hpp @@ -58,6 +58,10 @@ constexpr auto exp_impl(T x) noexcept { result = one / exp(-x); } + else if(x == one) + { + result = numbers::e_v; + } else { // Scale the argument to 0 < x < log(2). diff --git a/test/test_cbrt_from_math.cpp b/test/test_cbrt_from_math.cpp new file mode 100644 index 000000000..cae63a869 --- /dev/null +++ b/test/test_cbrt_from_math.cpp @@ -0,0 +1,259 @@ +// Copyright 2026 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "testing_config.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +namespace boost { namespace math { namespace policies { + +// Specialization of the precision structure. +template +struct precision +{ + using local_decimal_type = boost::decimal::decimal128_t; + + using precision_type = typename ThisPolicy::precision_type; + + using local_digits_2 = digits2<((static_cast(std::numeric_limits::digits10) + INTMAX_C(1)) * INTMAX_C(1000)) / INTMAX_C(301)>; // NOLINT(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers) + + using type = + typename std::conditional<((local_digits_2::value <= precision_type::value) || (precision_type::value <= 0)), + local_digits_2, + precision_type>::type; +}; + +} } } // namespace boost::math::policies + + +namespace local +{ + template + auto time_point() noexcept -> IntegralTimePointType + { + using local_integral_time_point_type = IntegralTimePointType; + using local_clock_type = ClockType; + + const auto current_now = + static_cast + ( + std::chrono::duration_cast + ( + local_clock_type::now().time_since_epoch() + ).count() + ); + + return static_cast(current_now); + } + + template + auto is_close_fraction(const NumericType& a, + const NumericType& b, + const NumericType& tol) noexcept -> bool + { + using std::fabs; + + auto result_is_ok = bool { }; + + if(b == static_cast(0)) + { + result_is_ok = (fabs(a - b) < tol); // LCOV_EXCL_LINE + } + else + { + const auto delta = fabs(1 - (a / b)); + + result_is_ok = (delta < tol); + } + + return result_is_ok; + } + + template + auto get_random_float_string(std::string* p_str_flt = nullptr, const bool do_seed_rnd_gens = false) -> ExtendedFloatType; // NOLINT(google-runtime-references) + + template + auto get_random_float_string(std::string* p_str_flt, const bool do_seed_rnd_gens) -> ExtendedFloatType // NOLINT(google-runtime-references) + { + using local_eng_exp_type = std::minstd_rand; + using local_eng_man_type = std::mt19937; + + static local_eng_exp_type eng_exp; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + static local_eng_man_type eng_man; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + + static std::uniform_int_distribution dst_exp { -4000, 4000 }; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + static std::uniform_int_distribution dst_man { 0x30U, 0x39U }; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + + if(do_seed_rnd_gens) + { + eng_exp.seed(time_point()); + eng_man.seed(time_point()); + } + + std::string str { }; + + using local_extended_float_type = ExtendedFloatType; + + + while(str.length() < static_cast(std::numeric_limits::digits10)) + { + if(str.length() == std::string::size_type { UINT8_C(0) }) + { + char chr_nonzero { }; + + do + { + chr_nonzero = static_cast(dst_man(eng_man)); + } + while(chr_nonzero == char { INT8_C(0x30) }); + + str += chr_nonzero; + } + else + { + str += static_cast(dst_man(eng_man)); + } + + if(str.length() == std::string::size_type { UINT8_C(1) }) + { + str += '.'; + } + } + + str += "E"; + + const int n_exp { dst_exp(eng_exp) }; + + std::stringstream strm { }; + + strm << std::showpos << n_exp; + + str += strm.str(); + + if(p_str_flt != nullptr) + { + *p_str_flt = str.c_str(); + } + + return local_extended_float_type { std::string_view(str.c_str()) }; + } +} // namespace local + +auto main() -> int +{ + using deci_type = boost::decimal::decimal128_t; + using ctrl_type = boost::multiprecision::number(std::numeric_limits::digits10)>, boost::multiprecision::et_off>; + + bool result_cbrt_ctrl_is_ok { true }; + + int index { }; + + #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) + constexpr int max_trials { 10000 }; + #else + constexpr int max_trials { 500 }; + #endif + + for(index = 0; index < max_trials; ++index) + { + const bool do_seed_rnd_gens { (index == 0) }; + + std::string str_flt { }; + + const deci_type + deci + { + local::get_random_float_string(&str_flt, do_seed_rnd_gens) + }; + + const ctrl_type ctrl { boost::math::cbrt(ctrl_type { str_flt }) }; + + const ctrl_type + test + { + [&str_flt] + { + const deci_type test { boost::math::cbrt(deci_type { str_flt }) }; + + std::stringstream strm { }; + + strm << std::setprecision(std::numeric_limits::digits10) + << test; + + return ctrl_type { strm.str() }; + }() + }; + + const ctrl_type + tol_one_eps_deci + { + []() + { + std::stringstream strm { }; + + strm << std::setprecision(std::numeric_limits::digits10) + << std::numeric_limits::epsilon(); + + return ctrl_type { strm.str() }; + }() + }; + + result_cbrt_ctrl_is_ok = local::is_close_fraction(ctrl, test, tol_one_eps_deci * 1024); + + BOOST_TEST(result_cbrt_ctrl_is_ok); + + if(!result_cbrt_ctrl_is_ok) + { + std::stringstream strm { }; + + strm << "str_flt: " + << std::setprecision(std::numeric_limits::digits10) + << str_flt + << ", test: " + << std::setprecision(std::numeric_limits::digits10) + << test + << ", ctrl: " + << std::setprecision(std::numeric_limits::digits10) + << ctrl + ; + + std::cout << strm.str() << std::endl; + + break; + } + } + + const bool result_total_is_ok { (result_cbrt_ctrl_is_ok && (index == max_trials)) }; + + BOOST_TEST(result_total_is_ok); + + return boost::report_errors(); +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma clang diagnostic pop +#endif diff --git a/test/test_cosh.cpp b/test/test_cosh.cpp index 4cc0719da..fa40f46fd 100644 --- a/test/test_cosh.cpp +++ b/test/test_cosh.cpp @@ -1,5 +1,5 @@ // Copyright 2023 -2024 Matt Borland -// Copyright 2023 -2024 Christopher Kormanyos +// Copyright 2023 -2026 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -217,6 +217,32 @@ namespace local result_is_ok = (result_val_zero_neg_is_ok && result_is_ok); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const auto val_cosh_one = + cosh + ( + decimal_type + { + static_cast + ( + ::my_one() + static_cast(dist(gen) - 1.0F) + ) + } + ); + + // N[Cosh[1], 40] + constexpr decimal_type local_cosh_one { "1.543080634815243778477905620757061682602" }; + + const auto result_val_one_is_ok = local::is_close_fraction(val_cosh_one, local_cosh_one, std::numeric_limits::epsilon() * 4); + + BOOST_TEST(result_val_one_is_ok); + + result_is_ok = (result_val_one_is_ok && result_is_ok); + } + return result_is_ok; } @@ -351,9 +377,9 @@ auto main() -> int const auto result_edge_is_ok = local::test_cosh_edge(); - const auto result_pos64_is_ok = local::test_cosh_64(64); + const auto result_pos64_is_ok = local::test_cosh_64(32); - const auto result_pos128_is_ok = local::test_cosh_128(400000); + const auto result_pos128_is_ok = local::test_cosh_128(32); BOOST_TEST(result_pos_is_ok); BOOST_TEST(result_neg_is_ok); @@ -390,5 +416,5 @@ auto main() -> int return (result_is_ok ? 0 : -1); } -auto my_zero() -> boost::decimal::decimal32_t& { static boost::decimal::decimal32_t val_zero { 0, 0 }; return val_zero; } -auto my_one () -> boost::decimal::decimal32_t& { static boost::decimal::decimal32_t val_one { 1, 0 }; return val_one; } +auto my_zero() -> boost::decimal::decimal32_t& { static boost::decimal::decimal32_t val_instance { 0 }; return val_instance; } +auto my_one () -> boost::decimal::decimal32_t& { static boost::decimal::decimal32_t val_instance { 1 }; return val_instance; } diff --git a/test/test_exp.cpp b/test/test_exp.cpp index ecf164cf3..68d837fa5 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -1,5 +1,5 @@ // Copyright 2023 - 2024 Matt Borland -// Copyright 2023 - 2024 Christopher Kormanyos +// Copyright 2023 - 2026 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -22,8 +22,8 @@ #include -template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0, 0 }; return val_zero; } -template auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_one { 1, 0 }; return val_one; } +template auto my_zero() -> DecimalType; +template auto my_one () -> DecimalType; namespace local { @@ -381,7 +381,7 @@ auto main() -> int } { - const auto result_pos128_is_ok = local::test_exp_128(8192); + const auto result_pos128_is_ok = local::test_exp_128(256); BOOST_TEST(result_pos128_is_ok); @@ -392,3 +392,6 @@ auto main() -> int return (result_is_ok ? 0 : -1); } + +template auto my_zero() -> DecimalType { using decimal_type = DecimalType; return decimal_type { 0 }; } +template auto my_one () -> DecimalType { using decimal_type = DecimalType; return decimal_type { 1 }; }