Skip to content

Commit

Permalink
Merge pull request #644 from cppalliance/dec64_faster
Browse files Browse the repository at this point in the history
  • Loading branch information
mborland authored Jun 13, 2024
2 parents ef8c6ed + 4e0b209 commit 00cd5ea
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 158 deletions.
203 changes: 91 additions & 112 deletions include/boost/decimal/decimal64_fast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class decimal64_fast final
public:
using significand_type = std::uint_fast64_t;
using exponent_type = std::uint_fast16_t;
using biased_exponent_type = std::int32_t;

private:
// In regular decimal64 we have to decode the significand end exponent
Expand All @@ -58,19 +59,19 @@ class decimal64_fast final
return sign_;
}

constexpr auto full_significand() const noexcept -> std::uint_fast64_t
constexpr auto full_significand() const noexcept -> significand_type
{
return significand_;
}

constexpr auto unbiased_exponent() const noexcept -> std::uint_fast16_t
constexpr auto unbiased_exponent() const noexcept -> exponent_type
{
return exponent_;
}

constexpr auto biased_exponent() const noexcept -> std::int32_t
constexpr auto biased_exponent() const noexcept -> biased_exponent_type
{
return static_cast<std::int32_t>(exponent_) - detail::bias_v<decimal64>;
return static_cast<biased_exponent_type>(exponent_) - detail::bias_v<decimal64>;
}

// Equality template between any integer type and decimal32
Expand Down Expand Up @@ -345,50 +346,33 @@ template <typename T1, typename T2, std::enable_if_t<detail::is_integral_v<T1> &
#endif
constexpr decimal64_fast::decimal64_fast(T1 coeff, T2 exp, bool sign) noexcept
{
// Older compilers have issues with conversions from __uint128, so we skip all that and use our uint128
#if defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__GNUC__) || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 10)) && (!defined(__clang__) || (defined(__clang__) && __clang_major__ < 13))
using Unsigned_Integer_1 = detail::make_unsigned_t<T1>;
using Unsigned_Integer = std::conditional_t<std::is_same<Unsigned_Integer_1, detail::uint128_t>::value, detail::uint128, Unsigned_Integer_1>;
#else
using Unsigned_Integer = detail::make_unsigned_t<T1>;
#endif

using Basis_Unsigned_Integer = std::conditional_t<std::numeric_limits<Unsigned_Integer>::digits10 < std::numeric_limits<significand_type>::digits10, significand_type, Unsigned_Integer>;

const bool isneg {coeff < static_cast<T1>(0) || sign};
sign_ = isneg;
Unsigned_Integer unsigned_coeff {detail::make_positive_unsigned(coeff)};

auto unsigned_coeff_digits {detail::num_digits(unsigned_coeff)};
const bool reduced {unsigned_coeff_digits > detail::precision_v<decimal64>};

// Strip digits and round as required
if (unsigned_coeff_digits > detail::precision_v<decimal64> + 1)
{
const auto digits_to_remove {unsigned_coeff_digits - (detail::precision_v<decimal64> + 1)};
auto unsigned_coeff {static_cast<Basis_Unsigned_Integer>(detail::make_positive_unsigned(coeff))};

#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wconversion"
#endif

unsigned_coeff /= detail::pow10(static_cast<Unsigned_Integer>(digits_to_remove));

#if defined(__GNUC__) && !defined(__clang__)
# pragma GCC diagnostic pop
#endif

exp += digits_to_remove;
unsigned_coeff_digits -= digits_to_remove;
}

// Round as required
if (reduced)
{
exp += static_cast<T2>(detail::fenv_round(unsigned_coeff, isneg));
}
// Normalize the value, so we don't have to worrya bout it with operations
detail::normalize<decimal64>(unsigned_coeff, exp, sign);

significand_ = static_cast<significand_type>(unsigned_coeff);

// Normalize the handling of zeros
if (significand_ == UINT32_C(0))
if (significand_ == UINT64_C(0))
{
exp = 0;
}

const auto biased_exp {static_cast<std::uint_fast32_t>(exp + detail::bias_v<decimal64>)};

if (biased_exp > detail::max_biased_exp_v<decimal64>)
{
significand_ = detail::d64_fast_inf;
Expand Down Expand Up @@ -498,8 +482,9 @@ constexpr auto operator==(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bo
}
#endif

return equal_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(),
rhs.full_significand(), rhs.biased_exponent(), rhs.isneg());
return lhs.sign_ == rhs.sign_ &&
lhs.exponent_ == rhs.exponent_ &&
lhs.significand_ == rhs.significand_;
}

template <typename Integer>
Expand Down Expand Up @@ -538,36 +523,45 @@ constexpr auto operator!=(Integer lhs, decimal64_fast rhs) noexcept
constexpr auto operator<(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool
{
#ifndef BOOST_DECIMAL_FAST_MATH
if (isnan(lhs) || isnan(rhs) ||
(!lhs.isneg() && rhs.isneg()))
if (isnan(lhs) || isnan(rhs))
{
return false;
}
#endif

if (!lhs.isneg() && rhs.isneg())
{
return false;
}
else if (lhs.isneg() && !rhs.isneg())

if (lhs.isneg() && !rhs.isneg())
{
return true;
}
else if (isfinite(lhs) && isinf(rhs))

#ifndef BOOST_DECIMAL_FAST_MATH
if (isfinite(lhs) && isinf(rhs))
{
return !signbit(rhs);
}
else if (isinf(lhs) && isfinite(rhs))

if (isinf(lhs) && isfinite(rhs))
{
return signbit(rhs);
}
#else
if (!lhs.isneg() && rhs.isneg())
#endif

if (lhs.significand_ == 0 || rhs.significand_ == 0)
{
return false;
return lhs.significand_ == 0 ? !rhs.sign_ : lhs.sign_;
}
else if (lhs.isneg() && !rhs.isneg())

if (lhs.exponent_ != rhs.exponent_)
{
return true;
return lhs.sign_ ? lhs.exponent_ > rhs.exponent_ : lhs.exponent_ < rhs.exponent_;
}
#endif

return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(),
rhs.full_significand(), rhs.biased_exponent(), rhs.isneg());
return lhs.sign_ ? lhs.significand_ > rhs.significand_ : lhs.significand_ < rhs.significand_;
}

template <typename Integer>
Expand Down Expand Up @@ -909,18 +903,10 @@ constexpr auto operator+(decimal64_fast lhs, decimal64_fast rhs) noexcept -> dec
return lhs - abs(rhs);
}

auto lhs_sig {lhs.full_significand()};
auto lhs_exp {lhs.biased_exponent()};
detail::normalize<decimal64>(lhs_sig, lhs_exp);

auto rhs_sig {rhs.full_significand()};
auto rhs_exp {rhs.biased_exponent()};
detail::normalize<decimal64>(rhs_sig, rhs_exp);

const auto result {detail::d64_add_impl<detail::decimal64_fast_components>(
lhs_sig, lhs_exp, lhs.isneg(),
rhs_sig, rhs_exp, rhs.isneg()
)};
lhs.significand_, lhs.biased_exponent(), lhs.sign_,
rhs.significand_, rhs.biased_exponent(), rhs.sign_
)};

return {result.sig, result.exp, result.sign};
}
Expand All @@ -943,10 +929,7 @@ constexpr auto operator+(decimal64_fast lhs, Integer rhs) noexcept
}
bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)};

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
detail::normalize<decimal64>(sig_lhs, exp_lhs);
auto lhs_components {detail::decimal64_fast_components{sig_lhs, exp_lhs, lhs.isneg()}};
auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}};

auto sig_rhs {static_cast<decimal64_fast::significand_type>(detail::make_positive_unsigned(rhs))};
std::int32_t exp_rhs {0};
Expand Down Expand Up @@ -1010,19 +993,11 @@ constexpr auto operator-(decimal64_fast lhs, decimal64_fast rhs) noexcept -> dec

const bool abs_lhs_bigger {abs(lhs) > abs(rhs)};

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
detail::normalize<decimal64>(sig_lhs, exp_lhs);

auto sig_rhs {rhs.full_significand()};
auto exp_rhs {rhs.biased_exponent()};
detail::normalize<decimal64>(sig_rhs, exp_rhs);

const auto result {detail::d64_sub_impl<detail::decimal64_fast_components>(
sig_lhs, exp_lhs, lhs.isneg(),
sig_rhs, exp_rhs, rhs.isneg(),
abs_lhs_bigger
)};
lhs.significand_, lhs.biased_exponent(), lhs.sign_,
rhs.significand_, rhs.biased_exponent(), rhs.sign_,
abs_lhs_bigger
)};

return {result.sig, result.exp, result.sign};
}
Expand All @@ -1045,10 +1020,7 @@ constexpr auto operator-(decimal64_fast lhs, Integer rhs) noexcept

const bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)};

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
detail::normalize<decimal64>(sig_lhs, exp_lhs);
auto lhs_components {detail::decimal64_fast_components{sig_lhs, exp_lhs, lhs.isneg()}};
auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}};

auto sig_rhs {static_cast<decimal64_fast::significand_type>(detail::make_positive_unsigned(rhs))};
std::int32_t exp_rhs {0};
Expand Down Expand Up @@ -1088,10 +1060,7 @@ constexpr auto operator-(Integer lhs, decimal64_fast rhs) noexcept
auto unsigned_sig_lhs = detail::shrink_significand<decimal64_fast::significand_type>(detail::make_positive_unsigned(sig_lhs), exp_lhs);
auto lhs_components {detail::decimal64_fast_components{unsigned_sig_lhs, exp_lhs, (lhs < 0)}};

auto sig_rhs {rhs.full_significand()};
auto exp_rhs {rhs.biased_exponent()};
detail::normalize<decimal64>(sig_rhs, exp_rhs);
auto rhs_components {detail::decimal64_fast_components{sig_rhs, exp_rhs, rhs.isneg()}};
auto rhs_components {detail::decimal64_fast_components{rhs.significand_, rhs.biased_exponent(), rhs.isneg()}};

const auto result {detail::d64_sub_impl<detail::decimal64_fast_components>(
lhs_components.sig, lhs_components.exp, lhs_components.sign,
Expand All @@ -1113,18 +1082,31 @@ constexpr auto operator*(decimal64_fast lhs, decimal64_fast rhs) noexcept -> dec
}
#endif

auto lhs_sig {lhs.full_significand()};
auto lhs_exp {lhs.biased_exponent()};
detail::normalize<decimal64>(lhs_sig, lhs_exp);
#if defined(__clang_major__) && __clang_major__ < 13

auto rhs_sig {rhs.full_significand()};
auto rhs_exp {rhs.biased_exponent()};
detail::normalize<decimal64>(rhs_sig, rhs_exp);

const auto result {detail::d64_mul_impl<detail::decimal64_fast_components>(lhs_sig, lhs_exp, lhs.isneg(),
rhs_sig, rhs_exp, rhs.isneg())};
const auto result {detail::d64_mul_impl<detail::decimal64_components>(
lhs.significand_, lhs.biased_exponent(), lhs.isneg(),
rhs.significand_, rhs.biased_exponent(), rhs.isneg()
)};

return {result.sig, result.exp, result.sign};

#else

#ifdef BOOST_DECIMAL_HAS_INT128
using unsigned_int128_type = boost::decimal::detail::uint128_t;
#else
using unsigned_int128_type = boost::decimal::detail::uint128;
#endif

auto res_sig {static_cast<unsigned_int128_type>(lhs.significand_) * static_cast<unsigned_int128_type>(rhs.significand_)};

auto res_exp {lhs.biased_exponent() + rhs.biased_exponent()};
bool sign {lhs.sign_ != rhs.sign_ && res_sig != 0};

return {res_sig, res_exp, sign};

#endif // Clang major check
}

template <typename Integer>
Expand All @@ -1138,10 +1120,7 @@ constexpr auto operator*(decimal64_fast lhs, Integer rhs) noexcept
}
#endif

auto lhs_sig {lhs.full_significand()};
auto lhs_exp {lhs.biased_exponent()};
detail::normalize<decimal64>(lhs_sig, lhs_exp);
auto lhs_components {detail::decimal64_fast_components{lhs_sig, lhs_exp, lhs.isneg()}};
auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}};

auto rhs_sig {static_cast<decimal64_fast::significand_type>(detail::make_positive_unsigned(rhs))};
std::int32_t rhs_exp {0};
Expand All @@ -1166,14 +1145,14 @@ constexpr auto operator*(Integer lhs, decimal64_fast rhs) noexcept

constexpr auto d64_fast_div_impl(decimal64_fast lhs, decimal64_fast rhs, decimal64_fast& q, decimal64_fast& r) noexcept -> void
{
const bool sign {lhs.isneg() != rhs.isneg()};

#ifndef BOOST_DECIMAL_FAST_MATH
// Check pre-conditions
constexpr decimal64_fast zero {0, 0};
constexpr decimal64_fast nan {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_snan, 0, false)};
constexpr decimal64_fast inf {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_inf, 0, false)};

const bool sign {lhs.isneg() != rhs.isneg()};

const auto lhs_fp {fpclassify(lhs)};
const auto rhs_fp {fpclassify(rhs)};

Expand Down Expand Up @@ -1215,28 +1194,28 @@ constexpr auto d64_fast_div_impl(decimal64_fast lhs, decimal64_fast rhs, decimal
static_cast<void>(r);
#endif

auto sig_lhs {lhs.full_significand()};
auto exp_lhs {lhs.biased_exponent()};
detail::normalize<decimal64>(sig_lhs, exp_lhs);

auto sig_rhs {rhs.full_significand()};
auto exp_rhs {rhs.biased_exponent()};
detail::normalize<decimal64>(sig_rhs, exp_rhs);

#ifdef BOOST_DECIMAL_DEBUG
std::cerr << "sig lhs: " << sig_lhs
<< "\nexp lhs: " << exp_lhs
<< "\nsig rhs: " << sig_rhs
<< "\nexp rhs: " << exp_rhs << std::endl;
#endif

detail::decimal64_fast_components lhs_components {sig_lhs, exp_lhs, lhs.isneg()};
detail::decimal64_fast_components rhs_components {sig_rhs, exp_rhs, rhs.isneg()};
detail::decimal64_fast_components q_components {};
#ifdef BOOST_DECIMAL_HAS_INT128
using unsigned_int128_type = boost::decimal::detail::uint128_t;
#else
using unsigned_int128_type = boost::decimal::detail::uint128;
#endif

detail::d64_generic_div_impl(lhs_components, rhs_components, q_components);
// If rhs is greater than we need to offset the significands to get the correct values
// e.g. 4/8 is 0 but 40/8 yields 5 in integer maths
constexpr auto tens_needed {detail::pow10(static_cast<unsigned_int128_type>(detail::precision_v<decimal64>))};
const auto big_sig_lhs {static_cast<unsigned_int128_type>(lhs.significand_) * tens_needed};

const auto res_sig {big_sig_lhs / static_cast<unsigned_int128_type>(rhs.significand_)};
const auto res_exp {(lhs.biased_exponent() - detail::precision_v<decimal64>) - rhs.biased_exponent()};

q = decimal64_fast(q_components.sig, q_components.exp, q_components.sign);
q = decimal64_fast{res_sig, res_exp, sign};
}

constexpr auto d64_fast_mod_impl(decimal64_fast lhs, decimal64_fast rhs, const decimal64_fast& q, decimal64_fast& r) noexcept -> void
Expand Down
Loading

0 comments on commit 00cd5ea

Please sign in to comment.