Skip to content

Commit 5be58d6

Browse files
committed
Handle pure powers of 10 in pow() function
1 parent 641435d commit 5be58d6

File tree

2 files changed

+119
-18
lines changed

2 files changed

+119
-18
lines changed

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

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -123,28 +123,53 @@ constexpr auto pow(T b, IntegralType p) noexcept
123123
{
124124
result = one;
125125
}
126-
else BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed<local_integral_type>::value)
126+
else
127127
{
128-
if(p < static_cast<local_integral_type>(UINT8_C(0)))
128+
int exp10val { };
129+
130+
const auto bn { frexp10(b, &exp10val) };
131+
132+
const auto
133+
zeros_removal
134+
{
135+
detail::remove_trailing_zeros(bn)
136+
};
137+
138+
const bool is_pure { static_cast<int>(zeros_removal.trimmed_number) == 1 };
139+
140+
if(is_pure)
129141
{
130-
const auto up =
131-
static_cast<local_unsigned_integral_type>
132-
(
133-
static_cast<local_unsigned_integral_type>(~p)
134-
+ static_cast<local_unsigned_integral_type>(UINT8_C(1))
135-
);
142+
// Here, a pure power-of-10 argument (b) gets a pure integral result.
143+
const int log10_val { exp10val + static_cast<int>(zeros_removal.number_of_removed_zeros) };
136144

137-
result = one / detail::pow_n_impl(b, up);
145+
result = T { 1, static_cast<int>(log10_val * static_cast<int>(p)) };
138146
}
139147
else
140148
{
141-
result = detail::pow_n_impl(b, static_cast<local_unsigned_integral_type>(p));
149+
BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed<local_integral_type>::value)
150+
{
151+
if(p < static_cast<local_integral_type>(UINT8_C(0)))
152+
{
153+
const auto up =
154+
static_cast<local_unsigned_integral_type>
155+
(
156+
static_cast<local_unsigned_integral_type>(~p)
157+
+ static_cast<local_unsigned_integral_type>(UINT8_C(1))
158+
);
159+
160+
result = one / detail::pow_n_impl(b, up);
161+
}
162+
else
163+
{
164+
result = detail::pow_n_impl(b, static_cast<local_unsigned_integral_type>(p));
165+
}
166+
}
167+
else
168+
{
169+
result = detail::pow_n_impl(b, static_cast<local_unsigned_integral_type>(p));
170+
}
142171
}
143172
}
144-
else
145-
{
146-
result = detail::pow_n_impl(b, static_cast<local_unsigned_integral_type>(p));
147-
}
148173
}
149174

150175
return result;

test/test_pow.cpp

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
#endif
2121
#include <boost/core/lightweight_test.hpp>
2222

23-
template<typename DecimalType> auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_zero { 0, 0 }; return my_zero; }
24-
template<typename DecimalType> auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_one { 1, 0 }; return my_one; }
25-
template<typename DecimalType> auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_inf { std::numeric_limits<decimal_type>::infinity() }; return my_inf; }
26-
template<typename DecimalType> auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_nan { std::numeric_limits<decimal_type>::quiet_NaN() }; return my_nan; }
23+
template<typename DecimalType> auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_val_zero { 0, 0 }; return my_val_zero; }
24+
template<typename DecimalType> auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_val_one { 1, 0 }; return my_val_one; }
25+
template<typename DecimalType> auto my_ten () -> DecimalType&;
26+
template<typename DecimalType> auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_val_inf { std::numeric_limits<decimal_type>::infinity() }; return my_val_inf; }
27+
template<typename DecimalType> auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_val_nan { std::numeric_limits<decimal_type>::quiet_NaN() }; return my_val_nan; }
2728

2829
namespace local
2930
{
@@ -304,6 +305,64 @@ namespace local
304305
return result_is_ok;
305306
}
306307

308+
template<typename DecimalType>
309+
auto test_10_pow_n() -> bool
310+
{
311+
using decimal_type = DecimalType;
312+
313+
using ctrl_val_array_type = std::array<decimal_type, static_cast<std::size_t>(UINT8_C(5))>;
314+
315+
bool result_is_ok { true };
316+
317+
{
318+
const ctrl_val_array_type ctrl_values =
319+
{{
320+
decimal_type { 1, 1},
321+
decimal_type { 1, 4},
322+
decimal_type { 1, 9},
323+
decimal_type { 1, 16},
324+
decimal_type { 1, 25},
325+
}};
326+
327+
for(auto i = 0; i < std::tuple_size<ctrl_val_array_type>::value; ++i)
328+
{
329+
const int n = i + 1;
330+
331+
decimal_type p10 = pow(pow(::my_ten<decimal_type>(), n), n);
332+
333+
const bool result_p10_is_ok = (p10 == ctrl_values[static_cast<std::size_t>(i)]);
334+
335+
result_is_ok = (result_p10_is_ok && result_is_ok);
336+
}
337+
}
338+
339+
{
340+
const ctrl_val_array_type ctrl_values =
341+
{{
342+
decimal_type { 1, -1},
343+
decimal_type { 1, -4},
344+
decimal_type { 1, -9},
345+
decimal_type { 1, -16},
346+
decimal_type { 1, -25},
347+
}};
348+
349+
for(auto i = 0; i < std::tuple_size<ctrl_val_array_type>::value; ++i)
350+
{
351+
const int n = i + 1;
352+
353+
decimal_type p10 = pow(pow(::my_ten<decimal_type>(), -n), n);
354+
355+
const bool result_p10_is_ok = (p10 == ctrl_values[static_cast<std::size_t>(i)]);
356+
357+
result_is_ok = (result_p10_is_ok && result_is_ok);
358+
}
359+
}
360+
361+
BOOST_TEST(result_is_ok);
362+
363+
return result_is_ok;
364+
}
365+
307366
template<typename DecimalType, typename FloatType>
308367
auto test_pow_edge() -> bool
309368
{
@@ -974,6 +1033,15 @@ namespace local
9741033
}
9751034
}
9761035

1036+
template<typename DecimalType> auto my_ten () -> DecimalType&
1037+
{
1038+
using decimal_type = DecimalType;
1039+
1040+
static decimal_type my_val_ten { 1, 1 };
1041+
1042+
return my_val_ten;
1043+
}
1044+
9771045
auto main() -> int
9781046
{
9791047
auto result_is_ok = true;
@@ -993,6 +1061,14 @@ auto main() -> int
9931061
result_is_ok = (result_test_pow_is_ok && result_is_ok);
9941062
}
9951063

1064+
{
1065+
using decimal_type = boost::decimal::decimal32;
1066+
1067+
const auto test_10_pow_n_is_ok = local::test_10_pow_n<decimal_type>();
1068+
1069+
result_is_ok = (test_10_pow_n_is_ok && result_is_ok);
1070+
}
1071+
9961072
{
9971073
using decimal_type = boost::decimal::decimal64;
9981074
using float_type = double;

0 commit comments

Comments
 (0)