Skip to content

Commit 641435d

Browse files
authored
Merge pull request #544 from cppalliance/log10
Log10
2 parents 6820459 + 6d5abe2 commit 641435d

File tree

7 files changed

+585
-70
lines changed

7 files changed

+585
-70
lines changed

.github/workflows/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ jobs:
6464
make gcov -f make_gcov_01_generic.gmk --jobs=8 MY_ALL_COV=0 MY_BOOST_ROOT=../../../boost-root MY_CC=${{ matrix.compiler }} MY_STD=${{ matrix.standard }}
6565
echo
6666
- name: upload-codecov
67-
uses: codecov/codecov-action@v3
67+
uses: codecov/codecov-action@v4
6868
with:
6969
files: ./test/cover/coverage.info
7070
token: ${{ secrets.CODECOV_TOKEN }}

include/boost/decimal/detail/cmath/log.hpp

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#define BOOST_DECIMAL_DETAIL_CMATH_LOG_HPP
88

99
#include <boost/decimal/fwd.hpp> // NOLINT(llvm-include-order)
10-
#include <boost/decimal/detail/cmath/impl/log_impl.hpp>
1110
#include <boost/decimal/detail/concepts.hpp>
1211
#include <boost/decimal/detail/config.hpp>
1312
#include <boost/decimal/detail/type_traits.hpp>
@@ -27,65 +26,33 @@ template <typename T>
2726
constexpr auto log_impl(T x) noexcept
2827
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T)
2928
{
30-
constexpr T zero { 0, 0 };
3129
constexpr T one { 1, 0 };
30+
constexpr T zero { 0, 0 };
3231

3332
T result { };
3433

35-
if (isnan(x))
34+
const auto fpc = fpclassify(x);
35+
36+
if (fpc == FP_ZERO)
3637
{
37-
result = x;
38+
result = -std::numeric_limits<T>::infinity();
3839
}
39-
else if (isinf(x))
40+
else if (signbit(x) || (fpc == FP_NAN))
4041
{
41-
result = (!signbit(x)) ? x: std::numeric_limits<T>::quiet_NaN();
42+
result = std::numeric_limits<T>::quiet_NaN();
43+
}
44+
else if (fpc == FP_INFINITE)
45+
{
46+
result = std::numeric_limits<T>::infinity();
4247
}
4348
else if (x < one)
4449
{
45-
// Handle reflection, the [+/-] zero-pole, and non-pole, negative x.
46-
if (x > zero)
47-
{
48-
result = -log(one / x);
49-
}
50-
else if ((x == zero) || (-x == zero))
51-
{
52-
// Actually, this should be equivalent to -HUGE_VAL.
53-
54-
result = -std::numeric_limits<T>::infinity();
55-
}
56-
else
57-
{
58-
result = std::numeric_limits<T>::quiet_NaN();
59-
}
50+
// Handle reflection.
51+
result = -log(one / x);
6052
}
6153
else if(x > one)
6254
{
63-
// The algorithm for logarithm is based on Chapter 5, pages 35-36
64-
// of Cody and Waite, Software Manual for the Elementary Functions,
65-
// Prentice Hall, 1980.
66-
67-
int exp2val { };
68-
69-
// TODO(ckormanyos) There is probably something more efficient than calling frexp here.
70-
auto g = frexp(x, &exp2val);
71-
72-
if (g < numbers::inv_sqrt2_v<T>)
73-
{
74-
g += g;
75-
76-
--exp2val;
77-
}
78-
79-
const auto s = (g - one) / (g + one);
80-
const auto z = s + s;
81-
const auto zsq = z * z;
82-
83-
result = z * fma(detail::log_series_expansion(zsq), zsq, one);
84-
85-
if (exp2val > 0)
86-
{
87-
result += static_cast<T>(exp2val * numbers::ln2_v<T>);
88-
}
55+
result = log10(x) * numbers::ln10_v<T>;
8956
}
9057
else
9158
{

include/boost/decimal/detail/cmath/log10.hpp

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77
#define BOOST_DECIMAL_DETAIL_CMATH_LOG10_HPP
88

99
#include <boost/decimal/fwd.hpp> // NOLINT(llvm-include-order)
10-
#include <boost/decimal/detail/type_traits.hpp>
10+
#include <boost/decimal/detail/cmath/impl/log_impl.hpp>
1111
#include <boost/decimal/detail/concepts.hpp>
1212
#include <boost/decimal/detail/config.hpp>
13+
#include <boost/decimal/detail/type_traits.hpp>
1314
#include <boost/decimal/numbers.hpp>
1415

1516
#ifndef BOOST_DECIMAL_BUILD_MODULE
@@ -26,21 +27,102 @@ template <typename T>
2627
constexpr auto log10_impl(T x) noexcept
2728
BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T)
2829
{
29-
// TODO(ckormanyos) Actually this is eintirely preliminary. The implementation
30-
// of log10 will/should-be entirely re-worked with specific base-10-relevant details.
31-
32-
// TODO(ckormanyos) Handle non-normal varguments.
33-
34-
// TODO(ckormanyos) Put in a basic check for pure powers of 10, resulting
35-
// in an exact result.
36-
37-
// Starting point: See implementation of log() and adapt to the following series.
38-
// Series[Log[10] Log[10, (1 + (z/2))/(1 - (z/2))], {z, 0, 17}]
39-
40-
return log(x) / numbers::ln10_v<T>;
30+
constexpr T zero { 0, 0 };
31+
constexpr T one { 1, 0 };
32+
33+
T result { };
34+
35+
const auto fpc = fpclassify(x);
36+
37+
if (fpc == FP_ZERO)
38+
{
39+
result = -std::numeric_limits<T>::infinity();
40+
}
41+
else if (signbit(x) || (fpc == FP_NAN))
42+
{
43+
result = std::numeric_limits<T>::quiet_NaN();
44+
}
45+
else if (fpc == FP_INFINITE)
46+
{
47+
result = std::numeric_limits<T>::infinity();
48+
}
49+
else
50+
{
51+
int exp10val { };
52+
53+
const auto gn { frexp10(x, &exp10val) };
54+
55+
const auto
56+
zeros_removal
57+
{
58+
remove_trailing_zeros(gn)
59+
};
60+
61+
const bool is_pure { static_cast<unsigned>(zeros_removal.trimmed_number) == 1U };
62+
63+
if(is_pure)
64+
{
65+
// Here, a pure power-of-10 argument gets a pure integral result.
66+
const int p10 { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
67+
68+
result = T { p10 };
69+
}
70+
else
71+
{
72+
if (x < one)
73+
{
74+
// Handle reflection.
75+
result = -log10(one / x);
76+
}
77+
else if(x > one)
78+
{
79+
// The algorithm for base-10 logarithm is based on Chapter 5,
80+
// pages 35-36 of Cody and Waite, "Software Manual for the
81+
// Elementary Functions", Prentice Hall, 1980.
82+
83+
// In this implementation, however, we use 2s (as for natural
84+
// logarithm in Cody and Waite) even though we are computing
85+
// the base-10 logarithm.
86+
87+
T g { gn, -std::numeric_limits<T>::digits10 };
88+
89+
exp10val += std::numeric_limits<T>::digits10;
90+
91+
int reduce_sqrt2 { };
92+
93+
while (g < numbers::inv_sqrt2_v<T>)
94+
{
95+
g += g;
96+
97+
++reduce_sqrt2;
98+
}
99+
100+
const T s { (g - one) / (g + one) };
101+
const T z { s + s };
102+
const T zsq { z * z };
103+
104+
result = z * fma(detail::log_series_expansion(zsq), zsq, one);
105+
106+
result /= numbers::ln10_v<T>;
107+
108+
for(int i = 0; i < reduce_sqrt2; ++i)
109+
{
110+
result -= numbers::log10_2_v<T>;
111+
}
112+
113+
result += static_cast<T>(exp10val);
114+
}
115+
else
116+
{
117+
result = zero;
118+
}
119+
}
120+
}
121+
122+
return result;
41123
}
42124

43-
} // namespace detail
125+
} //namespace detail
44126

45127
BOOST_DECIMAL_EXPORT template <typename T>
46128
constexpr auto log10(T x) noexcept

include/boost/decimal/numbers.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ BOOST_DECIMAL_EXPORT template <>
3838
BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 log10e_v<decimal128> = decimal128{detail::uint128{UINT64_C(235431510388986),
3939
UINT64_C(2047877485384264674)}, -34};
4040

41+
BOOST_DECIMAL_EXPORT template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Dec, std::enable_if_t<detail::is_decimal_floating_point_v<Dec>, bool> = true>
42+
BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log10_2_v = Dec{UINT64_C(3010299956639811952), -19};
43+
44+
BOOST_DECIMAL_EXPORT template <>
45+
BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 log10_2_v<decimal128> = decimal128{detail::uint128{UINT64_C(163188687641095),
46+
UINT64_C(3612628795761985410)}, -34};
47+
4148
BOOST_DECIMAL_EXPORT template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Dec, std::enable_if_t<detail::is_decimal_floating_point_v<Dec>, bool> = true>
4249
BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec pi_v = Dec{UINT64_C(3141592653589793238), -18};
4350

@@ -127,8 +134,9 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 phi_v<decimal128> = d
127134
UINT64_C(2061523135646567614)}, -33};
128135

129136
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto e {e_v<decimal64>};
130-
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log2e {log2e_v<decimal64>};
137+
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log10_2 {log10_2_v<decimal64>};
131138
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log10e {log10e_v<decimal64>};
139+
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log2e {log2e_v<decimal64>};
132140
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto pi {pi_v<decimal64>};
133141
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto inv_pi {inv_pi_v<decimal64>};
134142
BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto inv_sqrtpi {inv_sqrtpi_v<decimal64>};

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ run test_literals.cpp ;
107107
run test_lgamma.cpp ;
108108
run test_log.cpp ;
109109
run test_log1p.cpp ;
110+
run test_log10.cpp ;
110111
run test_pow.cpp ;
111112
run test_promotion.cpp ;
112113
run test_remainder_remquo.cpp ;

test/test_log.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,9 @@ auto main() -> int
452452
using decimal_type = boost::decimal::decimal32;
453453
using float_type = float;
454454

455-
const auto test_log_is_ok = local::test_log <decimal_type, float_type>(12);
456-
const auto test_log_between_1_and_2_is_ok = local::test_log_between_1_and_2<decimal_type, float_type>(24);
457-
const auto test_log_edge_is_ok = local::test_log_edge <decimal_type, float_type>(12);
455+
const auto test_log_is_ok = local::test_log <decimal_type, float_type>(32);
456+
const auto test_log_between_1_and_2_is_ok = local::test_log_between_1_and_2<decimal_type, float_type>(64);
457+
const auto test_log_edge_is_ok = local::test_log_edge <decimal_type, float_type>(32);
458458

459459
result_is_ok = (test_log_is_ok && test_log_between_1_and_2_is_ok && test_log_edge_is_ok && result_is_ok);
460460
}
@@ -463,9 +463,9 @@ auto main() -> int
463463
using decimal_type = boost::decimal::decimal64;
464464
using float_type = double;
465465

466-
const auto test_log_is_ok = local::test_log <decimal_type, float_type>(24);
467-
const auto test_log_between_1_and_2_is_ok = local::test_log_between_1_and_2<decimal_type, float_type>(256);
468-
const auto test_log_edge_is_ok = local::test_log_edge <decimal_type, float_type>(24);
466+
const auto test_log_is_ok = local::test_log <decimal_type, float_type>(64);
467+
const auto test_log_between_1_and_2_is_ok = local::test_log_between_1_and_2<decimal_type, float_type>(512);
468+
const auto test_log_edge_is_ok = local::test_log_edge <decimal_type, float_type>(64);
469469

470470
result_is_ok = (test_log_is_ok && test_log_between_1_and_2_is_ok && test_log_edge_is_ok && result_is_ok);
471471
}
@@ -479,7 +479,7 @@ auto main() -> int
479479
}
480480

481481
{
482-
const auto result_pos128_is_ok = local::test_log_128(1'400'000);
482+
const auto result_pos128_is_ok = local::test_log_128(800'000);
483483

484484
BOOST_TEST(result_pos128_is_ok);
485485

0 commit comments

Comments
 (0)