Skip to content

Commit 860c5e7

Browse files
authored
Merge pull request #689 from cppalliance/add
Improve 32-bit addition implementation
2 parents 5719f40 + 2a2c8f9 commit 860c5e7

File tree

5 files changed

+106
-92
lines changed

5 files changed

+106
-92
lines changed

include/boost/decimal/decimal32.hpp

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -842,22 +842,7 @@ constexpr auto operator+(decimal32 lhs, decimal32 rhs) noexcept -> decimal32
842842
}
843843
#endif
844844

845-
bool lhs_bigger {lhs > rhs};
846-
if (lhs.isneg() && rhs.isneg())
847-
{
848-
lhs_bigger = !lhs_bigger;
849-
}
850-
851-
// Ensure that lhs is always the larger for ease of implementation
852-
if (!lhs_bigger)
853-
{
854-
detail::swap(lhs, rhs);
855-
}
856-
857-
if (!lhs.isneg() && rhs.isneg())
858-
{
859-
return lhs - abs(rhs);
860-
}
845+
const bool abs_lhs_bigger {abs(lhs) > abs(rhs)};
861846

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

870-
return detail::add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
871-
sig_rhs, exp_rhs, rhs.isneg());
855+
return detail::d32_add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
856+
sig_rhs, exp_rhs, rhs.isneg(),
857+
abs_lhs_bigger);
872858
}
873859

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

888-
bool lhs_bigger {lhs > rhs};
889-
if (lhs.isneg() && (rhs < 0))
890-
{
891-
lhs_bigger = !lhs_bigger;
892-
}
893-
894874
// Make the significand type wide enough that it won't overflow during normalization
895875
auto sig_rhs {static_cast<promoted_significand_type>(detail::make_positive_unsigned(rhs))};
896-
bool abs_lhs_bigger {abs(lhs) > sig_rhs};
876+
const bool abs_lhs_bigger {abs(lhs) > sig_rhs};
897877

898878
auto sig_lhs {lhs.full_significand()};
899879
auto exp_lhs {lhs.biased_exponent()};
900880
detail::normalize(sig_lhs, exp_lhs);
901-
auto lhs_components {detail::decimal32_components{sig_lhs, exp_lhs, lhs.isneg()}};
902881

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

906885
// Now that the rhs has been normalized it is guaranteed to fit into the decimal32 significand type
907-
auto unsigned_sig_rhs {static_cast<typename detail::decimal32_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
908-
auto rhs_components {detail::decimal32_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}};
909-
910-
if (!lhs_bigger)
911-
{
912-
detail::swap(lhs_components, rhs_components);
913-
abs_lhs_bigger = !abs_lhs_bigger;
914-
}
886+
const auto final_sig_rhs {static_cast<typename detail::decimal32_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
915887

916-
if (!lhs_components.sign && rhs_components.sign)
917-
{
918-
return detail::sub_impl<decimal32>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
919-
rhs_components.sig, rhs_components.exp, rhs_components.sign,
888+
return detail::d32_add_impl<decimal32>(sig_lhs, exp_lhs, lhs.isneg(),
889+
final_sig_rhs, exp_rhs, (rhs < 0),
920890
abs_lhs_bigger);
921-
}
922-
else
923-
{
924-
return detail::add_impl<decimal32>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
925-
rhs_components.sig, rhs_components.exp, rhs_components.sign);
926-
}
927891
}
928892

929893
template <typename Integer>

include/boost/decimal/decimal32_fast.hpp

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -768,28 +768,11 @@ constexpr auto operator+(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec
768768
return res;
769769
}
770770
#endif
771-
772-
bool lhs_bigger {lhs > rhs};
773-
if (lhs.isneg() && rhs.isneg())
774-
{
775-
lhs_bigger = !lhs_bigger;
776-
}
777-
778-
// Ensure that lhs is always the larger for ease of implementation
779-
if (!lhs_bigger)
780-
{
781-
detail::swap(lhs, rhs);
782-
}
783-
784-
if (!lhs.isneg() && rhs.isneg())
785-
{
786-
return lhs - abs(rhs);
787-
}
788-
789-
return detail::add_impl<decimal32_fast>(
771+
772+
return detail::d32_add_impl<decimal32_fast>(
790773
lhs.significand_, lhs.biased_exponent(), lhs.sign_,
791-
rhs.significand_, rhs.biased_exponent(), rhs.sign_
792-
);
774+
rhs.significand_, rhs.biased_exponent(), rhs.sign_,
775+
(abs(lhs) > abs(rhs)));
793776
}
794777

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

809-
bool lhs_bigger {lhs > rhs};
810-
if (lhs.isneg() && (rhs < 0))
811-
{
812-
lhs_bigger = !lhs_bigger;
813-
}
814-
815792
auto sig_rhs {static_cast<promoted_significand_type>(detail::make_positive_unsigned(rhs))};
816-
bool abs_lhs_bigger {abs(lhs) > sig_rhs};
817-
818-
auto lhs_components {detail::decimal32_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}};
793+
const bool abs_lhs_bigger {abs(lhs) > sig_rhs};
819794

820795
exp_type exp_rhs {0};
821796
detail::normalize(sig_rhs, exp_rhs);
822-
auto unsigned_sig_rhs {static_cast<detail::decimal32_fast_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
823-
auto rhs_components {detail::decimal32_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}};
797+
const auto final_sig_rhs {static_cast<detail::decimal32_fast_components::significand_type>(detail::make_positive_unsigned(sig_rhs))};
824798

825-
if (!lhs_bigger)
826-
{
827-
detail::swap(lhs_components, rhs_components);
828-
abs_lhs_bigger = !abs_lhs_bigger;
829-
}
830-
831-
if (!lhs_components.sign && rhs_components.sign)
832-
{
833-
return detail::sub_impl<decimal32_fast>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
834-
rhs_components.sig, rhs_components.exp, rhs_components.sign,
799+
return detail::d32_add_impl<decimal32_fast>(lhs.significand_, lhs.biased_exponent(), lhs.sign_,
800+
final_sig_rhs, exp_rhs, (rhs < 0),
835801
abs_lhs_bigger);
836-
}
837-
else
838-
{
839-
return detail::add_impl<decimal32_fast>(lhs_components.sig, lhs_components.exp, lhs_components.sign,
840-
rhs_components.sig, rhs_components.exp, rhs_components.sign);
841-
}
842802
}
843803

844804
template <typename Integer>

include/boost/decimal/detail/add_impl.hpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,91 @@ namespace boost {
1717
namespace decimal {
1818
namespace detail {
1919

20+
template <typename ReturnType, typename T, typename U>
21+
BOOST_DECIMAL_FORCE_INLINE constexpr auto d32_add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
22+
T rhs_sig, U rhs_exp, bool rhs_sign,
23+
bool abs_lhs_bigger) noexcept -> ReturnType
24+
{
25+
using add_type = std::int_fast32_t;
26+
27+
auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp};
28+
auto signed_sig_lhs {static_cast<add_type>(detail::make_signed_value(lhs_sig, lhs_sign))};
29+
auto signed_sig_rhs {static_cast<add_type>(detail::make_signed_value(rhs_sig, rhs_sign))};
30+
31+
#ifdef BOOST_DECIMAL_DEBUG_ADD
32+
std::cerr << "Starting sig lhs: " << lhs_sig
33+
<< "\nStarting exp lhs: " << lhs_exp
34+
<< "\nStarting sig rhs: " << rhs_sig
35+
<< "\nStarting exp rhs: " << rhs_exp << std::endl;
36+
#endif
37+
38+
if (delta_exp > detail::precision + 1)
39+
{
40+
// If the difference in exponents is more than the digits of accuracy
41+
// we return the larger of the two
42+
//
43+
// e.g. 1e20 + 1e-20 = 1e20
44+
45+
#ifdef BOOST_DECIMAL_DEBUG_ADD
46+
std::cerr << "New sig: " << lhs_sig
47+
<< "\nNew exp: " << lhs_exp
48+
<< "\nNew neg: " << lhs_sign << std::endl;
49+
#endif
50+
51+
return abs_lhs_bigger ? ReturnType{lhs_sig, lhs_exp, lhs_sign} :
52+
ReturnType{rhs_sig, rhs_exp, rhs_sign};
53+
}
54+
55+
// The two numbers can be added together without special handling
56+
//
57+
// If we can add to the lhs sig rather than dividing we can save some precision
58+
// 32-bit signed int can have 9 digits and our normalized significand has 7
59+
60+
auto& sig_bigger {abs_lhs_bigger ? signed_sig_lhs : signed_sig_rhs};
61+
auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp};
62+
auto& sig_smaller {abs_lhs_bigger ? signed_sig_rhs : signed_sig_lhs};
63+
auto& sign_smaller {abs_lhs_bigger ? rhs_sign : lhs_sign};
64+
65+
if (delta_exp <= 2)
66+
{
67+
sig_bigger *= pow10(static_cast<std::remove_reference_t<decltype(sig_bigger)>>(delta_exp));
68+
exp_bigger -= delta_exp;
69+
delta_exp = 0;
70+
}
71+
else
72+
{
73+
sig_bigger *= 100;
74+
delta_exp -= 2;
75+
exp_bigger -=2;
76+
77+
if (delta_exp > 1)
78+
{
79+
sig_smaller /= pow10(static_cast<std::remove_reference_t<decltype(sig_smaller)>>(delta_exp - 1));
80+
delta_exp = 1;
81+
}
82+
}
83+
84+
if (delta_exp == 1)
85+
{
86+
detail::fenv_round(sig_smaller, sign_smaller);
87+
}
88+
89+
// Cast the results to signed types so that we can apply a sign at the end if necessary
90+
// Both of the significands are maximally 24 bits, so they fit into a 32-bit signed type just fine
91+
const auto new_sig {sig_bigger + sig_smaller};
92+
const auto new_exp {exp_bigger};
93+
const auto new_sign {new_sig < 0};
94+
const auto res_sig {detail::make_positive_unsigned(new_sig)};
95+
96+
#ifdef BOOST_DECIMAL_DEBUG_ADD
97+
std::cerr << "Final sig lhs: " << lhs_sig
98+
<< "\nFinal sig rhs: " << rhs_sig
99+
<< "\nResult sig: " << new_sig << std::endl;
100+
#endif
101+
102+
return {res_sig, new_exp, new_sign};
103+
}
104+
20105
template <typename ReturnType, typename T, typename U>
21106
BOOST_DECIMAL_FORCE_INLINE constexpr auto add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
22107
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> ReturnType

include/boost/decimal/detail/config.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,8 @@ typedef unsigned __int128 uint128_t;
292292
# define BOOST_DECIMAL_FORCE_INLINE inline
293293
#endif
294294

295+
#ifdef __FAST_MATH__
296+
# define BOOST_DECIMAL_FAST_MATH
297+
#endif
298+
295299
#endif // BOOST_DECIMAL_DETAIL_CONFIG_HPP

test/test_decimal32.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ int main()
536536
spot_check_addition(-1054191000, -920209700, -1974400700);
537537
spot_check_addition(353582500, -32044770, 321537730);
538538
spot_check_addition(989629100, 58451350, 1048080000);
539+
spot_check_addition(-531, -2347236, -2347767);
539540

540541
test_shrink_significand();
541542

0 commit comments

Comments
 (0)