Skip to content

Commit

Permalink
Merge pull request #689 from cppalliance/add
Browse files Browse the repository at this point in the history
Improve 32-bit addition implementation
  • Loading branch information
mborland authored Jul 1, 2024
2 parents 5719f40 + 2a2c8f9 commit 860c5e7
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 92 deletions.
52 changes: 8 additions & 44 deletions include/boost/decimal/decimal32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -842,22 +842,7 @@ constexpr auto operator+(decimal32 lhs, decimal32 rhs) noexcept -> decimal32
}
#endif

bool lhs_bigger {lhs > rhs};
if (lhs.isneg() && rhs.isneg())
{
lhs_bigger = !lhs_bigger;
}

// Ensure that lhs is always the larger for ease of implementation
if (!lhs_bigger)
{
detail::swap(lhs, rhs);
}

if (!lhs.isneg() && rhs.isneg())
{
return lhs - abs(rhs);
}
const bool abs_lhs_bigger {abs(lhs) > abs(rhs)};

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
Expand All @@ -867,8 +852,9 @@ constexpr auto operator+(decimal32 lhs, decimal32 rhs) noexcept -> decimal32
auto exp_rhs {rhs.biased_exponent()};
detail::normalize(sig_rhs, exp_rhs);

return detail::add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
sig_rhs, exp_rhs, rhs.isneg());
return detail::d32_add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
sig_rhs, exp_rhs, rhs.isneg(),
abs_lhs_bigger);
}

template <typename Integer>
Expand All @@ -885,45 +871,23 @@ constexpr auto operator+(decimal32 lhs, Integer rhs) noexcept
}
#endif

bool lhs_bigger {lhs > rhs};
if (lhs.isneg() && (rhs < 0))
{
lhs_bigger = !lhs_bigger;
}

// Make the significand type wide enough that it won't overflow during normalization
auto sig_rhs {static_cast<promoted_significand_type>(detail::make_positive_unsigned(rhs))};
bool abs_lhs_bigger {abs(lhs) > sig_rhs};
const bool abs_lhs_bigger {abs(lhs) > sig_rhs};

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
detail::normalize(sig_lhs, exp_lhs);
auto lhs_components {detail::decimal32_components{sig_lhs, exp_lhs, lhs.isneg()}};

exp_type exp_rhs {0};
detail::normalize(sig_rhs, exp_rhs);

// Now that the rhs has been normalized it is guaranteed to fit into the decimal32 significand type
auto unsigned_sig_rhs {static_cast<typename detail::decimal32_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
auto rhs_components {detail::decimal32_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}};

if (!lhs_bigger)
{
detail::swap(lhs_components, rhs_components);
abs_lhs_bigger = !abs_lhs_bigger;
}
const auto final_sig_rhs {static_cast<typename detail::decimal32_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};

if (!lhs_components.sign && rhs_components.sign)
{
return detail::sub_impl<decimal32>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
rhs_components.sig, rhs_components.exp, rhs_components.sign,
return detail::d32_add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
final_sig_rhs, exp_rhs, (rhs < 0),
abs_lhs_bigger);
}
else
{
return detail::add_impl<decimal32>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
rhs_components.sig, rhs_components.exp, rhs_components.sign);
}
}

template <typename Integer>
Expand Down
56 changes: 8 additions & 48 deletions include/boost/decimal/decimal32_fast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,28 +768,11 @@ constexpr auto operator+(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec
return res;
}
#endif

bool lhs_bigger {lhs > rhs};
if (lhs.isneg() && rhs.isneg())
{
lhs_bigger = !lhs_bigger;
}

// Ensure that lhs is always the larger for ease of implementation
if (!lhs_bigger)
{
detail::swap(lhs, rhs);
}

if (!lhs.isneg() && rhs.isneg())
{
return lhs - abs(rhs);
}

return detail::add_impl<decimal32_fast>(

return detail::d32_add_impl<decimal32_fast>(
lhs.significand_, lhs.biased_exponent(), lhs.sign_,
rhs.significand_, rhs.biased_exponent(), rhs.sign_
);
rhs.significand_, rhs.biased_exponent(), rhs.sign_,
(abs(lhs) > abs(rhs)));
}

template <typename Integer>
Expand All @@ -806,39 +789,16 @@ constexpr auto operator+(decimal32_fast lhs, Integer rhs) noexcept
}
#endif

bool lhs_bigger {lhs > rhs};
if (lhs.isneg() && (rhs < 0))
{
lhs_bigger = !lhs_bigger;
}

auto sig_rhs {static_cast<promoted_significand_type>(detail::make_positive_unsigned(rhs))};
bool abs_lhs_bigger {abs(lhs) > sig_rhs};

auto lhs_components {detail::decimal32_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}};
const bool abs_lhs_bigger {abs(lhs) > sig_rhs};

exp_type exp_rhs {0};
detail::normalize(sig_rhs, exp_rhs);
auto unsigned_sig_rhs {static_cast<detail::decimal32_fast_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
auto rhs_components {detail::decimal32_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}};
const auto final_sig_rhs {static_cast<detail::decimal32_fast_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};

if (!lhs_bigger)
{
detail::swap(lhs_components, rhs_components);
abs_lhs_bigger = !abs_lhs_bigger;
}

if (!lhs_components.sign && rhs_components.sign)
{
return detail::sub_impl<decimal32_fast>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
rhs_components.sig, rhs_components.exp, rhs_components.sign,
return detail::d32_add_impl<decimal32_fast>(lhs.significand_, lhs.biased_exponent(), lhs.sign_,
final_sig_rhs, exp_rhs, (rhs < 0),
abs_lhs_bigger);
}
else
{
return detail::add_impl<decimal32_fast>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
rhs_components.sig, rhs_components.exp, rhs_components.sign);
}
}

template <typename Integer>
Expand Down
85 changes: 85 additions & 0 deletions include/boost/decimal/detail/add_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,91 @@ namespace boost {
namespace decimal {
namespace detail {

template <typename ReturnType, typename T, typename U>
BOOST_DECIMAL_FORCE_INLINE constexpr auto d32_add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
T rhs_sig, U rhs_exp, bool rhs_sign,
bool abs_lhs_bigger) noexcept -> ReturnType
{
using add_type = std::int_fast32_t;

auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp};
auto signed_sig_lhs {static_cast<add_type>(detail::make_signed_value(lhs_sig, lhs_sign))};
auto signed_sig_rhs {static_cast<add_type>(detail::make_signed_value(rhs_sig, rhs_sign))};

#ifdef BOOST_DECIMAL_DEBUG_ADD
std::cerr << "Starting sig lhs: " << lhs_sig
<< "\nStarting exp lhs: " << lhs_exp
<< "\nStarting sig rhs: " << rhs_sig
<< "\nStarting exp rhs: " << rhs_exp << std::endl;
#endif

if (delta_exp > detail::precision + 1)
{
// If the difference in exponents is more than the digits of accuracy
// we return the larger of the two
//
// e.g. 1e20 + 1e-20 = 1e20

#ifdef BOOST_DECIMAL_DEBUG_ADD
std::cerr << "New sig: " << lhs_sig
<< "\nNew exp: " << lhs_exp
<< "\nNew neg: " << lhs_sign << std::endl;
#endif

return abs_lhs_bigger ? ReturnType{lhs_sig, lhs_exp, lhs_sign} :
ReturnType{rhs_sig, rhs_exp, rhs_sign};
}

// The two numbers can be added together without special handling
//
// If we can add to the lhs sig rather than dividing we can save some precision
// 32-bit signed int can have 9 digits and our normalized significand has 7

auto& sig_bigger {abs_lhs_bigger ? signed_sig_lhs : signed_sig_rhs};
auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp};
auto& sig_smaller {abs_lhs_bigger ? signed_sig_rhs : signed_sig_lhs};
auto& sign_smaller {abs_lhs_bigger ? rhs_sign : lhs_sign};

if (delta_exp <= 2)
{
sig_bigger *= pow10(static_cast<std::remove_reference_t<decltype(sig_bigger)>>(delta_exp));
exp_bigger -= delta_exp;
delta_exp = 0;
}
else
{
sig_bigger *= 100;
delta_exp -= 2;
exp_bigger -=2;

if (delta_exp > 1)
{
sig_smaller /= pow10(static_cast<std::remove_reference_t<decltype(sig_smaller)>>(delta_exp - 1));
delta_exp = 1;
}
}

if (delta_exp == 1)
{
detail::fenv_round(sig_smaller, sign_smaller);
}

// Cast the results to signed types so that we can apply a sign at the end if necessary
// Both of the significands are maximally 24 bits, so they fit into a 32-bit signed type just fine
const auto new_sig {sig_bigger + sig_smaller};
const auto new_exp {exp_bigger};
const auto new_sign {new_sig < 0};
const auto res_sig {detail::make_positive_unsigned(new_sig)};

#ifdef BOOST_DECIMAL_DEBUG_ADD
std::cerr << "Final sig lhs: " << lhs_sig
<< "\nFinal sig rhs: " << rhs_sig
<< "\nResult sig: " << new_sig << std::endl;
#endif

return {res_sig, new_exp, new_sign};
}

template <typename ReturnType, typename T, typename U>
BOOST_DECIMAL_FORCE_INLINE constexpr auto add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> ReturnType
Expand Down
4 changes: 4 additions & 0 deletions include/boost/decimal/detail/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,8 @@ typedef unsigned __int128 uint128_t;
# define BOOST_DECIMAL_FORCE_INLINE inline
#endif

#ifdef __FAST_MATH__
# define BOOST_DECIMAL_FAST_MATH
#endif

#endif // BOOST_DECIMAL_DETAIL_CONFIG_HPP
1 change: 1 addition & 0 deletions test/test_decimal32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,7 @@ int main()
spot_check_addition(-1054191000, -920209700, -1974400700);
spot_check_addition(353582500, -32044770, 321537730);
spot_check_addition(989629100, 58451350, 1048080000);
spot_check_addition(-531, -2347236, -2347767);

test_shrink_significand();

Expand Down

0 comments on commit 860c5e7

Please sign in to comment.