/////////////////////////////////////////////////////////////////////////////// // Copyright Christopher Kormanyos 2024 - 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) // #ifndef BOOST_MP_CPP_DF_QF_DETAIL_CCMATH_LOG_2024_12_30_HPP #define BOOST_MP_CPP_DF_QF_DETAIL_CCMATH_LOG_2024_12_30_HPP #include #include #include #include #if (defined(BOOST_GCC) && defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128)) // // This is the only way we can avoid // warning: non-standard suffix on floating constant [-Wpedantic] // when building with -Wall -pedantic. Neither __extension__ // nor #pragma diagnostic ignored work :( // #pragma GCC system_header #endif namespace boost { namespace multiprecision { namespace backends { namespace cpp_df_qf_detail { namespace ccmath { namespace detail { // LCOV_EXCL_START template constexpr auto exp_impl(Real x) noexcept -> Real { constexpr int my_digits_10 { ::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits10 }; constexpr int loop_count { my_digits_10 < 8 ? 7 : my_digits_10 < 16 ? 15 : my_digits_10 < 20 ? 19 : 33 }; // Scale the argument with a single factor of 2. // Then square the result upon return. x /= 2; // Perform a simple Taylor series of the exponent function. Real term { x }; Real sum { Real { 1 } + term }; for (int loop_index { INT8_C(2) }; loop_index < loop_count; ++loop_index) { term *= x; term /= static_cast(loop_index); sum += term; } // Scale the result. return sum * sum; } template constexpr auto log_impl_pade(Real x) noexcept -> typename std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits < 54), Real>::type { // PadeApproximant[Log[x], {x, 1, {8, 8}}] // FullSimplify[%] const Real top { ((static_cast(-1.0L) + x) * (static_cast(1.0L) + x) * (static_cast(761.0L) + x * (static_cast(28544.0L) + x * (static_cast(209305.0L) + x * (static_cast(423680.0L) + x * (static_cast(209305.0L) + x * (static_cast(28544.0L) + static_cast(761.0L) * x))))))) }; const Real bot { (static_cast(140.0L) * (static_cast(1.0L) + x * (static_cast(64.0L) + x * (static_cast(784.0L) + x * (static_cast(3136.0L) + x * (static_cast(4900.0L) + x * (static_cast(3136.0L) + x * (static_cast(784.0L) + x * (static_cast(64.0L) + x))))))))) }; return top / bot; } template constexpr auto log_impl_pade(Real x) noexcept -> typename std::enable_if<(!(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits < 54)), Real>::type { // PadeApproximant[Log[x], {x, 1, {16, 16}}] // FullSimplify[%] const Real top { (static_cast(17.0L) * (static_cast(-1.0L) + x) * (static_cast(1.0L) + x) * (static_cast(143327.0L) + x * (static_cast(25160192.0L) + x * (static_cast(1069458527.0L) + x * (static_cast(17931092992.0L) + x * (static_cast(144291009727.0L) + x * (static_cast(613705186816.0L) + x * (static_cast(1446475477311.0L) + x * (static_cast(1923749922816.0L) + x * (static_cast(1446475477311.0L) + x * (static_cast(613705186816.0L) + x * (static_cast(144291009727.0L) + x * (static_cast(17931092992.0L) + x * (static_cast(1069458527.0L) + x * (static_cast(25160192.0L) + static_cast(143327.0L) * x))))))))))))))) }; const Real bot { (static_cast(360360.0L) * (static_cast(1.0L) + x * (static_cast(256.0L) + x * (static_cast(14400.0L) + x * (static_cast(313600.0L) + x * (static_cast(3312400.0L) + x * (static_cast(19079424.0L) + x * (static_cast(64128064.0L) + x * (static_cast(130873600.0L) + x * (static_cast(165636900.0L) + x * (static_cast(130873600.0L) + x * (static_cast(64128064.0L) + x * (static_cast(19079424.0L) + x * (static_cast(3312400.0L) + x * (static_cast(313600.0L) + x * (static_cast(14400.0L) + x * (static_cast(256.0L) + x))))))))))))))))) }; return top / bot; } // N[Log[2], 101] // 0.69314718055994530941723212145817656807550013436025525412068000949339362196969471560586332699641868754 template constexpr auto constant_ln_two() noexcept -> typename ::std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits == 24), FloatingPointType>::type { return static_cast(0.69314718055994530941723212145817656807550013436025525412068L); } template constexpr auto constant_ln_two() noexcept -> typename ::std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits == 53), FloatingPointType>::type { return static_cast(0.69314718055994530941723212145817656807550013436025525412068L); } template constexpr auto constant_ln_two() noexcept -> typename ::std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits == 64), FloatingPointType>::type { return static_cast(0.69314718055994530941723212145817656807550013436025525412068L); } #if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128) template constexpr auto constant_ln_two() noexcept -> typename ::std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits == 113), FloatingPointType>::type { return static_cast(0.69314718055994530941723212145817656807550013436025525412068Q); } #else template constexpr auto constant_ln_two() noexcept -> typename ::std::enable_if<(::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::numeric_limits::digits == 113), FloatingPointType>::type { return static_cast(0.69314718055994530941723212145817656807550013436025525412068L); } #endif template constexpr auto log_impl(Real x) noexcept -> Real { int n2 { }; // Scale the argument down. Real x2 { ::boost::multiprecision::backends::cpp_df_qf_detail::ccmath::detail::frexp_impl(x, &n2) }; if (x2 > static_cast(0.875L)) { x2 /= 2; ++n2; } // Estimate the logarithm of the argument to roughly half // the precision of Real. const Real s { log_impl_pade(x2) }; // Compute the exponent function to the full precision of Real. const Real E { exp_impl(s) }; // Perform one single step of Newton-Raphson iteration // and scale the result back up. return (s + ((x2 - E) / E)) + Real { static_cast(n2) * constant_ln_two() }; } // LCOV_EXCL_STOP } // namespace detail template constexpr auto log(Real x) -> Real { if (BOOST_MP_IS_CONST_EVALUATED(x)) { return detail::log_impl(x); // LCOV_EXCL_LINE } else { using std::log; return log(x); } } } } } } } // namespace boost::multiprecision::backends::cpp_df_qf_detail::ccmath #endif // BOOST_MP_CPP_DF_QF_DETAIL_CCMATH_LOG_2024_12_30_HPP