Skip to content

Commit fd8b26f

Browse files
authored
Merge pull request #910 from cppalliance/improve_comp_bench
Improve benchmarks and minor fixes
2 parents d61d478 + 4854ada commit fd8b26f

32 files changed

+267
-96
lines changed

include/boost/decimal/detail/comparison.hpp

Lines changed: 130 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
namespace boost {
2727
namespace decimal {
2828

29+
#ifdef _MSC_VER
30+
#pragma warning(push)
31+
#pragma warning(disable:4127)
32+
#endif
33+
2934
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType>
3035
BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool
3136
{
@@ -75,75 +80,109 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, Decimal
7580
}
7681

7782
// Step 6: Normalize the significand and compare
78-
// Instead of multiplying the larger number, divide the smaller one
79-
if (delta_exp >= 0)
83+
BOOST_DECIMAL_IF_CONSTEXPR (detail::decimal_val_v<DecimalType> >= 128)
8084
{
81-
// Check if we can divide rhs_sig safely
82-
if (delta_exp > 0 && rhs_sig % detail::pow10(static_cast<comp_type>(delta_exp)) != 0U)
85+
// Instead of multiplying the larger number, divide the smaller one
86+
//
87+
// We try for multiplication even though it's a small range
88+
// Since it's an order of magnitude faster
89+
if (delta_exp <= 4 && delta_exp >= 4)
8390
{
84-
return false;
91+
if (delta_exp > 0)
92+
{
93+
lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp));
94+
}
95+
else if (delta_exp < 0)
96+
{
97+
rhs_sig *= detail::pow10(static_cast<comp_type>(-delta_exp));
98+
}
8599
}
86-
rhs_sig /= detail::pow10(static_cast<comp_type>(delta_exp));
100+
else if (delta_exp > 0)
101+
{
102+
// Check if we can divide rhs_sig safely
103+
// E.g. 9e0 != 90000000204928e-13 but if we just did division we would falsely get 9 ?= 9
104+
if (rhs_sig % detail::pow10(static_cast<comp_type>(delta_exp)) != 0U)
105+
{
106+
return false;
107+
}
108+
rhs_sig /= detail::pow10(static_cast<comp_type>(delta_exp));
109+
}
110+
else if (delta_exp < 0)
111+
{
112+
// Check if we can divide lhs_sig safely
113+
if (lhs_sig % detail::pow10(static_cast<comp_type>(-delta_exp)) != 0U)
114+
{
115+
return false;
116+
}
117+
lhs_sig /= detail::pow10(static_cast<comp_type>(-delta_exp));
118+
}
119+
120+
return lhs_sig == rhs_sig;
87121
}
88122
else
89123
{
90-
// Check if we can divide lhs_sig safely
91-
if (lhs_sig % detail::pow10(static_cast<comp_type>(-delta_exp)) != 0U)
124+
using promoted_sig_type = std::conditional_t<std::is_same<typename DecimalType::significand_type, std::uint32_t>::value, std::uint64_t, int128::uint128_t>;
125+
promoted_sig_type promotes_lhs {lhs_sig};
126+
promoted_sig_type promotes_rhs {rhs_sig};
127+
128+
if (delta_exp > 0)
92129
{
93-
return false;
130+
promotes_lhs *= detail::pow10(static_cast<promoted_sig_type>(delta_exp));
131+
}
132+
else if (delta_exp < 0)
133+
{
134+
promotes_rhs *= detail::pow10(static_cast<promoted_sig_type>(-delta_exp));
94135
}
95-
lhs_sig /= detail::pow10(static_cast<comp_type>(-delta_exp));
96-
}
97136

98-
return lhs_sig == rhs_sig;
137+
return promotes_lhs == promotes_rhs;
138+
}
99139
}
100140

141+
#ifdef _MSC_VER
142+
#pragma warning(pop)
143+
#endif
144+
101145
template <BOOST_DECIMAL_FAST_DECIMAL_FLOATING_TYPE DecimalType>
102146
BOOST_DECIMAL_FORCE_INLINE constexpr auto fast_equality_impl(const DecimalType& lhs, const DecimalType& rhs) noexcept -> bool
103147
{
104-
if (lhs.significand_ == 0U && rhs.significand_ == 0U)
105-
{
106-
// -0 == +0
107-
return true;
108-
}
109-
110-
if (lhs.exponent_ != rhs.exponent_)
111-
{
112-
return false;
113-
}
114-
if (lhs.significand_ != rhs.significand_)
148+
#ifndef BOOST_DECIMAL_FAST_MATH
149+
if (isnan(lhs) || isnan(rhs))
115150
{
116151
return false;
117152
}
153+
#endif
118154

119-
#ifndef BOOST_DECIMAL_FAST_MATH
120-
if (isnan(lhs))
155+
const auto lhs_sig {lhs.significand_};
156+
const auto rhs_sig {rhs.significand_};
157+
158+
if (lhs_sig == 0U && rhs_sig == 0U)
121159
{
122-
return false;
160+
// -0 == +0
161+
return true;
123162
}
124-
#endif
125163

126-
return lhs.sign_ == rhs.sign_;
164+
return lhs_sig == rhs_sig &&
165+
lhs.exponent_ == rhs.exponent_ &&
166+
lhs.sign_ == rhs.sign_;
127167
}
128168

129169
template <BOOST_DECIMAL_FAST_DECIMAL_FLOATING_TYPE DecimalType>
130170
BOOST_DECIMAL_FORCE_INLINE constexpr auto fast_inequality_impl(const DecimalType& lhs, const DecimalType& rhs) noexcept -> bool
131171
{
132-
return
133-
#ifndef BOOST_DECIMAL_FAST_MATH
134-
isnan(lhs) || isnan(rhs) ||
135-
#endif
136-
(lhs.sign_ != rhs.sign_) ||
137-
(lhs.exponent_ != rhs.exponent_) ||
138-
(lhs.significand_ != rhs.significand_);
172+
return !fast_equality_impl(lhs, rhs);
139173
}
140174

175+
#ifdef _MSC_VER
176+
#pragma warning(push)
177+
#pragma warning(disable:4127) // BOOST_DECIMAL_IF_CONSTEXPR prior to C++17 is constant conditional expression
178+
#endif
179+
141180
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
142181
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
143182
constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
144183
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<detail::is_ieee_type_v<DecimalType>, bool>
145184
{
146-
using comp_type = std::conditional_t<(std::numeric_limits<T1>::digits10 > std::numeric_limits<T2>::digits10), T1, T2>;
185+
using comp_type = detail::make_unsigned_t<std::conditional_t<(std::numeric_limits<T1>::digits10 > std::numeric_limits<T2>::digits10), T1, T2>>;
147186

148187
BOOST_DECIMAL_ASSERT(lhs_sig >= 0U);
149188
BOOST_DECIMAL_ASSERT(rhs_sig >= 0U);
@@ -154,14 +193,11 @@ constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
154193
return false;
155194
}
156195

157-
auto new_lhs_sig {static_cast<comp_type>(lhs_sig)};
158-
auto new_rhs_sig {static_cast<comp_type>(rhs_sig)};
159-
160196
const auto delta_exp {lhs_exp - rhs_exp};
161197

162198
// Check the value of delta exp to avoid to large a value for pow10
163199
// Also if only one of the significands is 0 then we know the values have to be mismatched
164-
if (new_lhs_sig == static_cast<comp_type>(0) && new_rhs_sig == static_cast<comp_type>(0))
200+
if (lhs_sig == 0U && rhs_sig == 0U)
165201
{
166202
return true;
167203
}
@@ -170,38 +206,73 @@ constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
170206
return false;
171207
}
172208

209+
auto new_lhs_sig {static_cast<comp_type>(lhs_sig)};
210+
auto new_rhs_sig {static_cast<comp_type>(rhs_sig)};
211+
173212
// Step 5: Normalize the significand and compare
174213
// Instead of multiplying the larger number, divide the smaller one
175-
if (delta_exp >= 0)
214+
BOOST_DECIMAL_IF_CONSTEXPR (detail::decimal_val_v<DecimalType> >= 128)
176215
{
177-
// Check if we can divide rhs_sig safely
178-
if (delta_exp > 0 && new_rhs_sig % detail::pow10(static_cast<comp_type>(delta_exp)) != 0U)
216+
// Instead of multiplying the larger number, divide the smaller one
217+
//
218+
// We try for multiplication even though it's a small range
219+
// Since it's an order of magnitude faster
220+
if (delta_exp <= 4 && delta_exp >= 4)
179221
{
180-
return false;
222+
if (delta_exp > 0)
223+
{
224+
new_lhs_sig *= detail::pow10(static_cast<comp_type>(delta_exp));
225+
}
226+
else if (delta_exp < 0)
227+
{
228+
new_rhs_sig *= detail::pow10(static_cast<comp_type>(-delta_exp));
229+
}
230+
}
231+
else if (delta_exp > 0)
232+
{
233+
// Check if we can divide rhs_sig safely
234+
// E.g. 9e0 != 90000000204928e-13 but if we just did division we would falsely get 9 ?= 9
235+
if (new_rhs_sig % detail::pow10(static_cast<comp_type>(delta_exp)) != 0U)
236+
{
237+
return false;
238+
}
239+
new_rhs_sig /= detail::pow10(static_cast<comp_type>(delta_exp));
181240
}
182-
new_rhs_sig /= detail::pow10(static_cast<comp_type>(delta_exp));
241+
else if (delta_exp < 0)
242+
{
243+
// Check if we can divide lhs_sig safely
244+
if (new_lhs_sig % detail::pow10(static_cast<comp_type>(-delta_exp)) != 0U)
245+
{
246+
return false;
247+
}
248+
new_lhs_sig /= detail::pow10(static_cast<comp_type>(-delta_exp));
249+
}
250+
251+
return new_lhs_sig == new_rhs_sig;
183252
}
184253
else
185254
{
186-
// Check if we can divide lhs_sig safely
187-
if (new_lhs_sig % detail::pow10(static_cast<comp_type>(-delta_exp)) != 0U)
255+
using promoted_sig_type = std::conditional_t<std::is_same<comp_type, std::uint32_t>::value, std::uint64_t, int128::uint128_t>;
256+
promoted_sig_type promotes_lhs {new_lhs_sig};
257+
promoted_sig_type promotes_rhs {new_rhs_sig};
258+
259+
if (delta_exp > 0)
188260
{
189-
return false;
261+
promotes_lhs *= detail::pow10(static_cast<promoted_sig_type>(delta_exp));
262+
}
263+
else if (delta_exp < 0)
264+
{
265+
promotes_rhs *= detail::pow10(static_cast<promoted_sig_type>(-delta_exp));
190266
}
191-
new_lhs_sig /= detail::pow10(static_cast<comp_type>(-delta_exp));
192-
}
193-
194-
#ifdef BOOST_DECIMAL_DEBUG_EQUAL
195-
std::cerr << "Normalized Values"
196-
<< "\nlhs_sig: " << new_lhs_sig
197-
<< "\nlhs_exp: " << lhs_exp
198-
<< "\nrhs_sig: " << new_rhs_sig
199-
<< "\nrhs_exp: " << rhs_exp << std::endl;
200-
#endif
201267

202-
return new_lhs_sig == new_rhs_sig;
268+
return promotes_lhs == promotes_rhs;
269+
}
203270
}
204271

272+
#ifdef _MSC_VER
273+
#pragma warning(pop)
274+
#endif
275+
205276
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE DecimalType = decimal32, BOOST_DECIMAL_INTEGRAL T1,
206277
BOOST_DECIMAL_INTEGRAL U1, BOOST_DECIMAL_INTEGRAL T2, BOOST_DECIMAL_INTEGRAL U2>
207278
constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,

include/boost/decimal/detail/mul_impl.hpp

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -193,38 +193,22 @@ template <typename ReturnType, BOOST_DECIMAL_INTEGRAL T1, BOOST_DECIMAL_INTEGRAL
193193
constexpr auto d128_mul_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
194194
T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> ReturnType
195195
{
196-
bool sign {lhs_sign != rhs_sign};
196+
const bool sign {lhs_sign != rhs_sign};
197197

198-
const auto lhs_dig {detail::num_digits(lhs_sig)};
199-
const auto rhs_dig {detail::num_digits(rhs_sig)};
198+
auto res_sig {detail::umul256(lhs_sig, rhs_sig)};
199+
auto res_exp {lhs_exp + rhs_exp};
200200

201-
// TODO(mborland): Benchmark this since counting digits is a horribly slow operation
202-
// If we can avoid it don't do 256 bit multiplication because it is slow
203-
if (lhs_dig + rhs_dig <= std::numeric_limits<boost::int128::uint128_t>::digits10)
201+
const auto sig_dig {detail::num_digits(res_sig)};
202+
203+
if (sig_dig > std::numeric_limits<boost::int128::uint128_t>::digits10)
204204
{
205-
auto res_sig {lhs_sig * rhs_sig};
206-
auto res_exp {lhs_exp + rhs_exp};
207-
return {res_sig, res_exp, sign};
205+
const auto digit_delta {sig_dig - std::numeric_limits<boost::int128::uint128_t>::digits10};
206+
res_sig /= pow10(int128::uint128_t(digit_delta));
207+
res_exp += digit_delta;
208208
}
209-
else
210-
{
211-
// Once we have the normalized significands and exponents all we have to do is
212-
// multiply the significands and add the exponents
213-
auto res_sig {detail::umul256(lhs_sig, rhs_sig)};
214-
auto res_exp {lhs_exp + rhs_exp};
215-
216-
const auto sig_dig {detail::num_digits(res_sig)};
217209

218-
if (sig_dig > std::numeric_limits<boost::int128::uint128_t>::digits10)
219-
{
220-
const auto digit_delta {sig_dig - std::numeric_limits<boost::int128::uint128_t>::digits10};
221-
res_sig /= pow10(int128::uint128_t(digit_delta));
222-
res_exp += digit_delta;
223-
}
224-
225-
BOOST_DECIMAL_ASSERT((res_sig[3] | res_sig[2]) == 0U);
226-
return {int128::uint128_t{res_sig[1], res_sig[0]}, res_exp, sign};
227-
}
210+
BOOST_DECIMAL_ASSERT((res_sig[3] | res_sig[2]) == 0U);
211+
return {int128::uint128_t{res_sig[1], res_sig[0]}, res_exp, sign};
228212
}
229213

230214
template <typename ReturnType, typename T>

test/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ if(HAVE_BOOST_TEST)
1010
find_package(fmt)
1111
if(fmt_FOUND)
1212
message(STATUS "Boost.Decimal: Test with fmtlib")
13-
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::decimal Boost::core Boost::math Boost::multiprecision Boost::charconv fmt::fmt COMPILE_DEFINITIONS BOOST_DECIMAL_TEST_FMT)
13+
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::decimal Boost::core Boost::math Boost::multiprecision Boost::charconv Boost::random fmt::fmt COMPILE_DEFINITIONS BOOST_DECIMAL_TEST_FMT)
1414
else()
1515
message(STATUS "Boost.Decimal: Test without fmtlib")
16-
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::decimal Boost::core Boost::math Boost::multiprecision Boost::charconv)
16+
boost_test_jamfile(FILE Jamfile LINK_LIBRARIES Boost::decimal Boost::core Boost::math Boost::multiprecision Boost::charconv Boost::random)
1717
endif()
1818

1919
endif()

0 commit comments

Comments
 (0)