// Copyright 2023 - 2026 Matt Borland // Copyright 2023 - 2026 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_DECIMAL_DETAIL_CMATH_POW_HPP #define BOOST_DECIMAL_DETAIL_CMATH_POW_HPP #include // NOLINT(llvm-include-order) #include #include #include #include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include #include #endif namespace boost { namespace decimal { BOOST_DECIMAL_EXPORT template constexpr auto pow(const T b, const IntegralType p) noexcept BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, T, detail::is_integral_v, IntegralType, T) { using local_integral_type = IntegralType; constexpr T zero { 0, 0 }; constexpr T one { 1, 0 }; T result { }; const auto fpc_x = fpclassify(b); const auto p_is_odd = (static_cast(p & 1) != static_cast(0)); if (p == static_cast(0)) { // pow(base, +/-0) returns 1 for any base, even when base is NaN. // Excluded from LCOV since it's apparently optimized away or otherwise // missing from LCOV. Verified this line is covered in the unit tests. result = one; // LCOV_EXCL_LINE } else if (fpc_x == FP_ZERO) { // pow( +0, exp), where exp is a negative odd integer, returns +infinity. // pow( -0, exp), where exp is a negative odd integer, returns +infinity. // pow(+/-0, exp), where exp is a negative even integer, returns +infinity. // pow( +0, exp), where exp is a positive odd integer, returns +0. // pow( -0, exp), where exp is a positive odd integer, returns -0. // pow(+/-0, exp), where exp is a positive even integer, returns +0. result = ((p < static_cast(0)) ? std::numeric_limits::infinity() : ((p_is_odd && signbit(b)) ? -zero : zero)); } #ifndef BOOST_DECIMAL_FAST_MATH else if (fpc_x == FP_INFINITE) { if (signbit(b)) { if (p < static_cast(0)) { // pow(-infinity, exp) returns -0 if exp is a negative odd integer. // pow(-infinity, exp) returns +0 if exp is a negative even integer. result = (p_is_odd ? -zero : zero); } else { // pow(-infinity, exp) returns -infinity if exp is a positive odd integer. // pow(-infinity, exp) returns +infinity if exp is a positive even integer. result = (p_is_odd ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); } } else { // pow(+infinity, exp) returns +0 for any negative exp. // pow(+infinity, exp) returns +infinity for any positive exp. result = ((p < static_cast(0)) ? zero : std::numeric_limits::infinity()); } } else if (fpc_x != FP_NORMAL) { // Excluded from LCOV since it's apparently optimized away or otherwise // missing from LCOV. Verified this line is covered in the unit tests. result = std::numeric_limits::quiet_NaN(); // LCOV_EXCL_LINE } #endif else { using local_unsigned_integral_type = std::make_unsigned_t; int exp10val { }; const auto bn { frexp10(b, &exp10val) }; const auto zeros_removal { detail::remove_trailing_zeros(bn) }; const bool is_pure { static_cast(zeros_removal.trimmed_number) == 1 }; if(is_pure) { // Here, a pure power-of-10 argument (b) gets a pure integral result. const int log10_val { exp10val + static_cast(zeros_removal.number_of_removed_zeros) }; result = T { 1, log10_val * static_cast(p) }; } else { BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) { if(p < static_cast(UINT8_C(0))) { const auto up = static_cast ( static_cast(~p) + static_cast(UINT8_C(1)) ); result = one / detail::pow_n_impl(b, up); } else { result = detail::pow_n_impl(b, static_cast(p)); } } else { result = detail::pow_n_impl(b, static_cast(p)); } } } return result; } BOOST_DECIMAL_EXPORT template constexpr auto pow(const T x, const T a) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { constexpr T zero { 0, 0 }; constexpr T one { 1, 0 }; auto result = zero; const auto fpc_a = fpclassify(a); const auto na = static_cast(a); if (fpc_a == FP_ZERO) { // pow(base, +/-0) returns 1 for any base, even when base is NaN. result = one; } else if (na == a) { result = pow(x, na); } else { const auto fpc_x = fpclassify(x); if ((fpc_x == FP_NAN) || (fpc_a == FP_NAN)) { // This line is known to be covered by tests. result = std::numeric_limits::quiet_NaN(); // LCOV_EXCL_LINE } else if (fpc_x == FP_ZERO) { #ifndef BOOST_DECIMAL_FAST_MATH if ((fpc_a == FP_NORMAL) || (fpc_a == FP_INFINITE)) { // pow(+/-0, exp), where exp is negative and finite, returns +infinity. // pow(+/-0, exp), where exp is positive non-integer, returns +0. // pow(+/-0, -infinity) returns +infinity. // pow(+/-0, +infinity) returns +0. result = (signbit(a) ? std::numeric_limits::infinity() : zero); } #else if (fpc_a == FP_NORMAL) { result = T{0}; } #endif } #ifndef BOOST_DECIMAL_FAST_MATH else if (fpc_x == FP_INFINITE) { if ((fpc_a == FP_NORMAL) || (fpc_a == FP_INFINITE)) { // pow(+infinity, exp) returns +0 for any negative exp. // pow(-infinity, exp) returns +infinity for any positive exp. result = (signbit(a) ? zero : std::numeric_limits::infinity()); } } #endif else { if (fpc_a == FP_ZERO) { result = one; } #ifndef BOOST_DECIMAL_FAST_MATH else if (fpc_a == FP_INFINITE) { result = ( (fabs(x) < one) ? (signbit(a) ? std::numeric_limits::infinity() : zero) : (fabs(x) > one) ? (signbit(a) ? zero : std::numeric_limits::infinity()) : one ); } #endif else { const T a_log_x { a * log(x) }; result = exp(a_log_x); } } } return result; } } // namespace decimal } // namespace boost #endif // BOOST_DECIMAL_DETAIL_CMATH_POW_HPP