Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions include/boost/decimal/detail/cmath/cosh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> };

result = (exp_pos_val + (one / exp_pos_val)) / two;
result = (local_cosh_one + (one / local_cosh_one)) / 2;
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions include/boost/decimal/detail/cmath/exp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ constexpr auto exp_impl(T x) noexcept
{
result = one / exp(-x);
}
else if(x == one)
{
result = numbers::e_v<T>;
}
else
{
// Scale the argument to 0 < x < log(2).
Expand Down
259 changes: 259 additions & 0 deletions test/test_cbrt_from_math.cpp
Original file line number Diff line number Diff line change
@@ -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 <chrono>
#include <iomanip>
#include <iostream>
#include <limits>
#include <random>
#include <type_traits>

#include <boost/decimal.hpp>
#include <boost/math/policies/policy.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>

#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 <boost/core/lightweight_test.hpp>

namespace boost { namespace math { namespace policies {

// Specialization of the precision structure.
template<typename ThisPolicy>
struct precision<boost::decimal::decimal128_t, ThisPolicy>
{
using local_decimal_type = boost::decimal::decimal128_t;

using precision_type = typename ThisPolicy::precision_type;

using local_digits_2 = digits2<((static_cast<std::intmax_t>(std::numeric_limits<local_decimal_type>::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<typename IntegralTimePointType,
typename ClockType = std::chrono::high_resolution_clock>
auto time_point() noexcept -> IntegralTimePointType
{
using local_integral_time_point_type = IntegralTimePointType;
using local_clock_type = ClockType;

const auto current_now =
static_cast<std::uintmax_t>
(
std::chrono::duration_cast<std::chrono::nanoseconds>
(
local_clock_type::now().time_since_epoch()
).count()
);

return static_cast<local_integral_time_point_type>(current_now);
}

template<typename NumericType>
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<NumericType>(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<typename ExtendedFloatType>
auto get_random_float_string(std::string* p_str_flt = nullptr, const bool do_seed_rnd_gens = false) -> ExtendedFloatType; // NOLINT(google-runtime-references)

template<typename ExtendedFloatType>
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<signed> dst_exp { -4000, 4000 }; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
static std::uniform_int_distribution<unsigned> dst_man { 0x30U, 0x39U }; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

if(do_seed_rnd_gens)
{
eng_exp.seed(time_point<typename local_eng_exp_type::result_type>());
eng_man.seed(time_point<typename local_eng_man_type::result_type>());
}

std::string str { };

using local_extended_float_type = ExtendedFloatType;


while(str.length() < static_cast<std::string::size_type>(std::numeric_limits<local_extended_float_type>::digits10))
{
if(str.length() == std::string::size_type { UINT8_C(0) })
{
char chr_nonzero { };

do
{
chr_nonzero = static_cast<char>(dst_man(eng_man));
}
while(chr_nonzero == char { INT8_C(0x30) });

str += chr_nonzero;
}
else
{
str += static_cast<char>(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<boost::multiprecision::cpp_dec_float<static_cast<unsigned int>(std::numeric_limits<deci_type>::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<boost::decimal::decimal128_t>(&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<boost::decimal::decimal128_t>::digits10)
<< test;

return ctrl_type { strm.str() };
}()
};

const ctrl_type
tol_one_eps_deci
{
[]()
{
std::stringstream strm { };

strm << std::setprecision(std::numeric_limits<boost::decimal::decimal128_t>::digits10)
<< std::numeric_limits<deci_type>::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<deci_type>::digits10)
<< str_flt
<< ", test: "
<< std::setprecision(std::numeric_limits<deci_type>::digits10)
<< test
<< ", ctrl: "
<< std::setprecision(std::numeric_limits<deci_type>::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
36 changes: 31 additions & 5 deletions test/test_cosh.cpp
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -217,6 +217,32 @@ namespace local
result_is_ok = (result_val_zero_neg_is_ok && result_is_ok);
}

for(auto i = static_cast<unsigned>(UINT8_C(0)); i < static_cast<unsigned>(UINT8_C(4)); ++i)
{
static_cast<void>(i);

const auto val_cosh_one =
cosh
(
decimal_type
{
static_cast<int>
(
::my_one() + static_cast<decimal_type>(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<decimal_type>::epsilon() * 4);

BOOST_TEST(result_val_one_is_ok);

result_is_ok = (result_val_one_is_ok && result_is_ok);
}

return result_is_ok;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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; }
11 changes: 7 additions & 4 deletions test/test_exp.cpp
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -22,8 +22,8 @@

#include <boost/core/lightweight_test.hpp>

template<typename DecimalType> auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0, 0 }; return val_zero; }
template<typename DecimalType> auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_one { 1, 0 }; return val_one; }
template<typename DecimalType> auto my_zero() -> DecimalType;
template<typename DecimalType> auto my_one () -> DecimalType;

namespace local
{
Expand Down Expand Up @@ -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);

Expand All @@ -392,3 +392,6 @@ auto main() -> int

return (result_is_ok ? 0 : -1);
}

template<typename DecimalType> auto my_zero() -> DecimalType { using decimal_type = DecimalType; return decimal_type { 0 }; }
template<typename DecimalType> auto my_one () -> DecimalType { using decimal_type = DecimalType; return decimal_type { 1 }; }
Loading