// Copyright 2023 Matt Borland // Copyright 2023 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_DECIMAL_DETAIL_CMATH_LOG10_HPP #define BOOST_DECIMAL_DETAIL_CMATH_LOG10_HPP #include // NOLINT(llvm-include-order) #include #include #include #include #include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include #endif namespace boost { namespace decimal { namespace detail { template constexpr auto log10_impl(const T x) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { T result { }; const auto fpc = fpclassify(x); if (fpc == FP_ZERO) { #ifndef BOOST_DECIMAL_FAST_MATH result = -std::numeric_limits::infinity(); #else result = T{0}; #endif } else if (signbit(x)) { #ifndef BOOST_DECIMAL_FAST_MATH result = std::numeric_limits::quiet_NaN(); #else result = T{0}; #endif } else if (fpc != FP_NORMAL) { result = x; } else { int exp10val { }; const auto gn { frexp10(x, &exp10val) }; const auto zeros_removal { remove_trailing_zeros(gn) }; const bool is_pure { zeros_removal.trimmed_number == 1U }; if(is_pure) { // Here, a pure power-of-10 argument gets a pure integral result. const int p10 { exp10val + static_cast(zeros_removal.number_of_removed_zeros) }; result = T { p10 }; } else { constexpr T one { 1 }; if (x < one) { // Handle reflection. result = -log10(one / x); } else if(x > one) { // The algorithm for base-10 logarithm is based on Chapter 5, // pages 35-36 of Cody and Waite, "Software Manual for the // Elementary Functions", Prentice Hall, 1980. // In this implementation, however, we use 2s (as for natural // logarithm in Cody and Waite) even though we are computing // the base-10 logarithm. T g { gn, -std::numeric_limits::digits10 }; exp10val += std::numeric_limits::digits10; int reduce_sqrt2 { }; while (g < numbers::inv_sqrt2_v) { g += g; ++reduce_sqrt2; } const T s { (g - one) / (g + one) }; const T z { s + s }; const T zsq { z * z }; result = z * fma(detail::log_series_expansion(zsq), zsq, one); result /= numbers::ln10_v; for(int i = 0; i < reduce_sqrt2; ++i) { result -= numbers::log10_2_v; } result += static_cast(exp10val); } } } return result; } } //namespace detail BOOST_DECIMAL_EXPORT template constexpr auto log10(const T x) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { using evaluation_type = detail::evaluation_type_t; return static_cast(detail::log10_impl(static_cast(x))); } } // namespace decimal } // namespace boost #endif // BOOST_DECIMAL_DETAIL_CMATH_LOG10_HPP