Skip to content

Commit 58d8d29

Browse files
committed
Add new 128-bit addition impl
1 parent 4f9b37c commit 58d8d29

File tree

1 file changed

+79
-0
lines changed

1 file changed

+79
-0
lines changed

include/boost/decimal/detail/add_impl.hpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,85 @@ constexpr auto d128_add_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign,
340340
return {new_sig, new_exp, sign};
341341
}
342342

343+
template <typename ReturnType, typename T, typename U>
344+
constexpr auto d128_add_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
345+
T rhs_sig, U rhs_exp, bool rhs_sign,
346+
bool abs_lhs_bigger) noexcept -> ReturnType
347+
{
348+
auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp};
349+
350+
#ifdef BOOST_DECIMAL_DEBUG_ADD
351+
std::cerr << "Starting sig lhs: " << lhs_sig
352+
<< "\nStarting exp lhs: " << lhs_exp
353+
<< "\nStarting sig rhs: " << rhs_sig
354+
<< "\nStarting exp rhs: " << rhs_exp << std::endl;
355+
#endif
356+
357+
if (delta_exp > detail::precision_v<decimal128> + 1)
358+
{
359+
// If the difference in exponents is more than the digits of accuracy
360+
// we return the larger of the two
361+
//
362+
// e.g. 1e20 + 1e-20 = 1e20
363+
364+
return abs_lhs_bigger ? ReturnType{lhs_sig, lhs_exp, lhs_sign} :
365+
ReturnType{rhs_sig, rhs_exp, rhs_sign};
366+
}
367+
368+
// The two numbers can be added together without special handling
369+
//
370+
// If we can add to the lhs sig rather than dividing we can save some precision
371+
// 32-bit signed int can have 9 digits and our normalized significand has 7
372+
373+
auto& sig_bigger {abs_lhs_bigger ? lhs_sig : rhs_sig};
374+
auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp};
375+
auto& sig_smaller {abs_lhs_bigger ? rhs_sig : lhs_sig};
376+
auto& sign_smaller {abs_lhs_bigger ? rhs_sign : lhs_sign};
377+
auto& sign_bigger {abs_lhs_bigger ? lhs_sign : rhs_sign};
378+
379+
if (delta_exp <= 2)
380+
{
381+
sig_bigger *= pow10(static_cast<uint128>(delta_exp));
382+
exp_bigger -= delta_exp;
383+
delta_exp = 0;
384+
}
385+
else
386+
{
387+
sig_bigger *= 100;
388+
delta_exp -= 2;
389+
exp_bigger -= 2;
390+
391+
if (delta_exp > 1)
392+
{
393+
sig_smaller /= pow10(static_cast<uint128>(delta_exp - 1));
394+
delta_exp = 1;
395+
}
396+
397+
if (delta_exp == 1)
398+
{
399+
detail::fenv_round<decimal128>(sig_smaller, sign_smaller);
400+
}
401+
}
402+
403+
// Cast the results to signed types so that we can apply a sign at the end if necessary
404+
// Both of the significands are maximally 24 bits, so they fit into a 32-bit signed type just fine
405+
auto signed_sig_lhs {detail::make_signed_value(sig_bigger, sign_bigger)};
406+
auto signed_sig_rhs {detail::make_signed_value(sig_smaller, sign_smaller)};
407+
408+
const auto new_sig {signed_sig_lhs + signed_sig_rhs};
409+
const auto new_exp {exp_bigger};
410+
const auto new_sign {new_sig < 0};
411+
const auto res_sig {detail::make_positive_unsigned(new_sig)};
412+
413+
#ifdef BOOST_DECIMAL_DEBUG_ADD
414+
std::cerr << "Final sig lhs: " << lhs_sig
415+
<< "\nFinal sig rhs: " << rhs_sig
416+
<< "\nResult sig: " << new_sig << std::endl;
417+
#endif
418+
419+
return {res_sig, new_exp, new_sign};
420+
}
421+
343422
#ifdef _MSC_VER
344423
# pragma warning(pop)
345424
#endif

0 commit comments

Comments
 (0)