Skip to content

Commit 37a578c

Browse files
committed
Prelim finish of log10 dedicated impl
1 parent edf3757 commit 37a578c

File tree

3 files changed

+184
-16
lines changed

3 files changed

+184
-16
lines changed

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

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,39 @@ constexpr auto log10_impl(T x) noexcept
3232

3333
T result { };
3434

35-
if (isnan(x))
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))
3642
{
37-
result = x;
43+
result = std::numeric_limits<T>::quiet_NaN();
3844
}
39-
else if (isinf(x))
45+
else if (fpc == FP_INFINITE)
4046
{
41-
result = (!signbit(x)) ? x: std::numeric_limits<T>::quiet_NaN();
47+
result = std::numeric_limits<T>::infinity();
4248
}
4349
else
4450
{
4551
int exp10val { };
4652

47-
T g { frexp10(x, &exp10val) };
53+
auto gn { frexp10(x, &exp10val) };
4854

49-
while(g > one)
50-
{
51-
g /= 10;
52-
53-
++exp10val;
54-
}
55+
const auto zeros_removal_result { detail::remove_trailing_zeros(gn) };
5556

56-
const bool is_pure = (g == one);
57+
const bool is_pure { (zeros_removal_result.trimmed_number == one) };
5758

5859
if(is_pure)
5960
{
60-
result = T { exp10val };
61+
// Here, a pure power-of-10 argument gets a pure integral result.
62+
result =
63+
T
64+
{
65+
exp10val
66+
+ static_cast<int>(zeros_removal_result.number_of_removed_zeros)
67+
};
6168
}
6269
else
6370
{
@@ -70,8 +77,6 @@ constexpr auto log10_impl(T x) noexcept
7077
}
7178
else if ((x == zero) || (-x == zero))
7279
{
73-
// Actually, this should be equivalent to -HUGE_VAL.
74-
7580
result = -std::numeric_limits<T>::infinity();
7681
}
7782
else
@@ -84,6 +89,11 @@ constexpr auto log10_impl(T x) noexcept
8489
// The algorithm for base-10 logarithm is based on Chapter 5, pages 35-36
8590
// of Cody and Waite, Software Manual for the Elementary Functions,
8691
// Prentice Hall, 1980.
92+
93+
T g { T { gn } / pow10(std::numeric_limits<T>::digits10) };
94+
95+
exp10val += std::numeric_limits<T>::digits10;
96+
8797
constexpr T inv_sqrt10 { UINT64_C(3162277660168379332), -19 };
8898

8999
const bool reduce_sqrt10 { g < inv_sqrt10 };
@@ -118,6 +128,7 @@ constexpr auto log10_impl(T x) noexcept
118128
}
119129
}
120130
}
131+
121132
return result;
122133
}
123134

test/Jamfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ run test_legendre.cpp ;
106106
run test_literals.cpp ;
107107
run test_lgamma.cpp ;
108108
run test_log.cpp ;
109-
run test_log10.cpp ;
110109
run test_log1p.cpp ;
110+
run test_log10.cpp ;
111111
run test_pow.cpp ;
112112
run test_promotion.cpp ;
113113
run test_remainder_remquo.cpp ;

test/test_log10.cpp

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222

2323
#include <boost/core/lightweight_test.hpp>
2424

25+
template<typename DecimalType> auto my_zero() -> DecimalType&;
26+
template<typename DecimalType> auto my_one () -> DecimalType&;
27+
template<typename DecimalType> auto my_inf () -> DecimalType&;
28+
2529
namespace local
2630
{
2731
template<typename IntegralTimePointType,
@@ -182,6 +186,114 @@ namespace local
182186
return result_is_ok;
183187
}
184188

189+
template<typename DecimalType, typename FloatType>
190+
auto test_log10_edge() -> bool
191+
{
192+
using decimal_type = DecimalType;
193+
using float_type = FloatType;
194+
195+
std::mt19937_64 gen;
196+
197+
gen.seed(time_point<typename std::mt19937_64::result_type>());
198+
199+
std::uniform_real_distribution<float_type> dist(1.0F, 2.0F);
200+
201+
volatile auto result_is_ok = true;
202+
203+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
204+
{
205+
static_cast<void>(index);
206+
207+
const auto log_zero = log10(::my_zero<decimal_type>() * static_cast<decimal_type>(dist(gen)));
208+
209+
const volatile auto result_log_zero_is_ok = (isinf(log_zero) && (log_zero < ::my_zero<decimal_type>()));
210+
211+
BOOST_TEST(result_log_zero_is_ok);
212+
213+
result_is_ok = (result_log_zero_is_ok && result_is_ok);
214+
}
215+
216+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
217+
{
218+
static_cast<void>(index);
219+
220+
const auto log_zero_minus = log10(-::my_zero<decimal_type>() * static_cast<decimal_type>(dist(gen)));
221+
222+
const volatile auto result_log_zero_minus_is_ok = (isinf(log_zero_minus) && (log_zero_minus < ::my_zero<decimal_type>()));
223+
224+
BOOST_TEST(result_log_zero_minus_is_ok);
225+
226+
result_is_ok = (result_log_zero_minus_is_ok && result_is_ok);
227+
}
228+
229+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
230+
{
231+
static_cast<void>(index);
232+
233+
const auto log_one = log10(::my_one<decimal_type>());
234+
235+
const volatile auto result_log_one_is_ok = (log_one == ::my_zero<decimal_type>() * static_cast<decimal_type>(dist(gen)));
236+
237+
BOOST_TEST(result_log_one_is_ok);
238+
239+
result_is_ok = (result_log_one_is_ok && result_is_ok);
240+
}
241+
242+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
243+
{
244+
static_cast<void>(index);
245+
246+
const auto log_one_minus = log10(-::my_one<decimal_type>());
247+
248+
const volatile auto result_log_one_minus_is_ok = isnan(log_one_minus);
249+
250+
BOOST_TEST(result_log_one_minus_is_ok);
251+
252+
result_is_ok = (result_log_one_minus_is_ok && result_is_ok);
253+
}
254+
255+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
256+
{
257+
static_cast<void>(index);
258+
259+
const auto log_inf = log10(::my_inf<decimal_type>() * static_cast<decimal_type>(dist(gen)));
260+
261+
const volatile auto result_log_inf_is_ok = isinf(log_inf);
262+
263+
BOOST_TEST(result_log_inf_is_ok);
264+
265+
result_is_ok = (result_log_inf_is_ok && result_is_ok);
266+
}
267+
268+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
269+
{
270+
static_cast<void>(index);
271+
272+
const auto log_inf_minus = log10(-::my_inf<decimal_type>() * static_cast<decimal_type>(dist(gen)));
273+
274+
const volatile auto result_log_inf_minus_is_ok = isnan(log_inf_minus);
275+
276+
BOOST_TEST(result_log_inf_minus_is_ok);
277+
278+
result_is_ok = (result_log_inf_minus_is_ok && result_is_ok);
279+
}
280+
281+
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(UINT8_C(4)); ++index)
282+
{
283+
static_cast<void>(index);
284+
285+
const auto log_nan = log10(std::numeric_limits<decimal_type>::quiet_NaN() * static_cast<decimal_type>(dist(gen)));
286+
287+
const volatile auto result_log_nan_is_ok = isnan(log_nan);
288+
289+
BOOST_TEST(result_log_nan_is_ok);
290+
291+
result_is_ok = (result_log_nan_is_ok && result_is_ok);
292+
}
293+
294+
return result_is_ok;
295+
}
296+
185297
} // namespace local
186298

187299
auto main() -> int
@@ -194,6 +306,8 @@ auto main() -> int
194306

195307
const auto test_log10_is_ok = local::test_log10<decimal_type, float_type>(128);
196308

309+
BOOST_TEST(test_log10_is_ok);
310+
197311
result_is_ok = (test_log10_is_ok && result_is_ok);
198312
}
199313

@@ -203,10 +317,53 @@ auto main() -> int
203317

204318
const auto test_log10_pow10_is_ok = local::test_log10_pow10<decimal_type, float_type>();
205319

320+
BOOST_TEST(test_log10_pow10_is_ok);
321+
206322
result_is_ok = (test_log10_pow10_is_ok && result_is_ok);
207323
}
208324

325+
{
326+
using decimal_type = boost::decimal::decimal32;
327+
using float_type = float;
328+
329+
const auto test_log10_edge_is_ok = local::test_log10_edge<decimal_type, float_type>();
330+
331+
BOOST_TEST(test_log10_edge_is_ok);
332+
333+
result_is_ok = (test_log10_edge_is_ok && result_is_ok);
334+
}
335+
209336
result_is_ok = ((boost::report_errors() == 0) && result_is_ok);
210337

211338
return (result_is_ok ? 0 : -1);
212339
}
340+
341+
template<typename DecimalType>
342+
auto my_zero() -> DecimalType&
343+
{
344+
using decimal_type = DecimalType;
345+
346+
static decimal_type val_zero { 0, 0 };
347+
348+
return val_zero;
349+
}
350+
351+
template<typename DecimalType>
352+
auto my_one() -> DecimalType&
353+
{
354+
using decimal_type = DecimalType;
355+
356+
static decimal_type val_one { 1, 0 };
357+
358+
return val_one;
359+
}
360+
361+
template<typename DecimalType>
362+
auto my_inf() -> DecimalType&
363+
{
364+
using decimal_type = DecimalType;
365+
366+
static decimal_type val_inf { std::numeric_limits<decimal_type>::infinity() };
367+
368+
return val_inf;
369+
}

0 commit comments

Comments
 (0)