Skip to content

Commit 973b3f3

Browse files
authored
Merge pull request #576 from cppalliance/mixed
2 parents 10089df + e4bdfdd commit 973b3f3

File tree

12 files changed

+1931
-308
lines changed

12 files changed

+1931
-308
lines changed

include/boost/decimal/decimal32.hpp

Lines changed: 25 additions & 282 deletions
Large diffs are not rendered by default.

include/boost/decimal/decimal32_fast.hpp

Lines changed: 579 additions & 7 deletions
Large diffs are not rendered by default.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
// Copyright 2023 - 2024 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_DETAIL_ADD_IMPL_HPP
6+
#define BOOST_DECIMAL_DETAIL_ADD_IMPL_HPP
7+
8+
#include <boost/decimal/detail/attributes.hpp>
9+
#include <boost/decimal/detail/apply_sign.hpp>
10+
#include <boost/decimal/detail/fenv_rounding.hpp>
11+
12+
#ifndef BOOST_DECIMAL_BUILD_MODULE
13+
#include <cstdint>
14+
#endif
15+
16+
namespace boost {
17+
namespace decimal {
18+
namespace detail {
19+
20+
template <typename ReturnType, typename T1, typename T2>
21+
constexpr auto add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign,
22+
T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> ReturnType
23+
{
24+
const bool sign {lhs_sign};
25+
26+
auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp};
27+
28+
#ifdef BOOST_DECIMAL_DEBUG_ADD
29+
std::cerr << "Starting sig lhs: " << lhs_sig
30+
<< "\nStarting exp lhs: " << lhs_exp
31+
<< "\nStarting sig rhs: " << rhs_sig
32+
<< "\nStarting exp rhs: " << rhs_exp << std::endl;
33+
#endif
34+
35+
if (delta_exp > detail::precision + 1)
36+
{
37+
// If the difference in exponents is more than the digits of accuracy
38+
// we return the larger of the two
39+
//
40+
// e.g. 1e20 + 1e-20 = 1e20
41+
42+
#ifdef BOOST_DECIMAL_DEBUG_ADD
43+
std::cerr << "New sig: " << lhs_sig
44+
<< "\nNew exp: " << lhs_exp
45+
<< "\nNew neg: " << lhs_sign << std::endl;
46+
#endif
47+
48+
return {static_cast<std::uint32_t>(lhs_sig), lhs_exp, lhs_sign};
49+
}
50+
51+
// The two numbers can be added together without special handling
52+
//
53+
// If we can add to the lhs sig rather than dividing we can save some precision
54+
// 32-bit signed int can have 9 digits and our normalized significand has 7
55+
if (delta_exp <= 2)
56+
{
57+
lhs_sig *= pow10(static_cast<T1>(delta_exp));
58+
lhs_exp -= delta_exp;
59+
delta_exp = 0;
60+
}
61+
else
62+
{
63+
lhs_sig *= 100;
64+
delta_exp -= 2;
65+
lhs_exp -=2;
66+
67+
if (delta_exp > 1)
68+
{
69+
rhs_sig /= pow10(static_cast<T2>(delta_exp - 1));
70+
delta_exp = 1;
71+
}
72+
}
73+
74+
if (delta_exp == 1)
75+
{
76+
detail::fenv_round(rhs_sig, rhs_sign);
77+
}
78+
79+
// Cast the results to signed types so that we can apply a sign at the end if necessary
80+
// Both of the significands are maximally 24 bits, so they fit into a 32-bit signed type just fine
81+
const auto new_sig {static_cast<std::int32_t>(lhs_sig + rhs_sig)};
82+
const auto new_exp {lhs_exp};
83+
const auto res_sig {detail::make_positive_unsigned(new_sig)};
84+
85+
#ifdef BOOST_DECIMAL_DEBUG_ADD
86+
std::cerr << "Final sig lhs: " << lhs_sig
87+
<< "\nFinal sig rhs: " << rhs_sig
88+
<< "\nResult sig: " << new_sig << std::endl;
89+
#endif
90+
91+
return {res_sig, new_exp, sign};
92+
}
93+
94+
} // namespace detail
95+
} // namespace decimal
96+
} // namespace boost
97+
98+
#endif //BOOST_DECIMAL_DETAIL_ADD_IMPL_HPP

include/boost/decimal/detail/cmath/fma.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#define BOOST_DECIMAL_DETAIL_CMATH_IMPL_FMA_HPP
77

88
#include <boost/decimal/decimal32.hpp>
9+
#include <boost/decimal/decimal32_fast.hpp>
910
#include <boost/decimal/decimal64.hpp>
1011
#include <boost/decimal/decimal128.hpp>
1112
#include <boost/decimal/detail/config.hpp>
@@ -32,7 +33,7 @@ constexpr auto fmad32(decimal32 x, decimal32 y, decimal32 z) noexcept -> decimal
3233
auto exp_rhs {y.biased_exponent()};
3334
detail::normalize(sig_rhs, exp_rhs);
3435

35-
auto mul_result {mul_impl(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())};
36+
auto mul_result {detail::mul_impl<detail::decimal32_components>(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())};
3637
const decimal32 dec_result {mul_result.sig, mul_result.exp, mul_result.sign};
3738

3839
const auto res_add {detail::check_non_finite(dec_result, z)};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2023 - 2024 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_DETAIL_DIV_IMPL_HPP
6+
#define BOOST_DECIMAL_DETAIL_DIV_IMPL_HPP
7+
8+
#ifndef BOOST_DECIMAL_BUILD_MODULE
9+
#include <limits>
10+
#include <cstdint>
11+
#endif
12+
13+
namespace boost {
14+
namespace decimal {
15+
namespace detail {
16+
17+
template <typename T>
18+
constexpr auto generic_div_impl(const T& lhs, const T& rhs, T& q) noexcept -> void
19+
{
20+
bool sign {lhs.sign != rhs.sign};
21+
22+
// If rhs is greater than we need to offset the significands to get the correct values
23+
// e.g. 4/8 is 0 but 40/8 yields 5 in integer maths
24+
const auto big_sig_lhs {static_cast<std::uint64_t>(lhs.sig) * detail::pow10(detail::precision)};
25+
26+
auto res_sig {big_sig_lhs / static_cast<std::uint64_t>(rhs.sig)};
27+
auto res_exp {(lhs.exp - detail::precision) - rhs.exp};
28+
29+
const auto sig_dig {detail::num_digits(res_sig)};
30+
31+
if (sig_dig > std::numeric_limits<std::uint32_t>::digits10)
32+
{
33+
res_sig /= detail::pow10(static_cast<std::uint64_t>(sig_dig - std::numeric_limits<std::uint32_t>::digits10));
34+
res_exp += sig_dig - std::numeric_limits<std::uint32_t>::digits10;
35+
}
36+
37+
const auto res_sig_32 {static_cast<std::uint32_t>(res_sig)};
38+
39+
#ifdef BOOST_DECIMAL_DEBUG
40+
std::cerr << "\nres sig: " << res_sig_32
41+
<< "\nres exp: " << res_exp << std::endl;
42+
#endif
43+
44+
if (res_sig_32 == 0)
45+
{
46+
sign = false;
47+
}
48+
49+
// Let the constructor handle shrinking it back down and rounding correctly
50+
q = T{res_sig_32, res_exp, sign};
51+
}
52+
53+
} // namespace detail
54+
} // namespace decimal
55+
} // namespace boost
56+
57+
#endif //BOOST_DECIMAL_DETAIL_DIV_IMPL_HPP
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2023 - 2024 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_DETAIL_MUL_IMPL_HPP
6+
#define BOOST_DECIMAL_DETAIL_MUL_IMPL_HPP
7+
8+
#include <boost/decimal/detail/attributes.hpp>
9+
#include <boost/decimal/detail/apply_sign.hpp>
10+
#include <boost/decimal/detail/fenv_rounding.hpp>
11+
12+
#ifndef BOOST_DECIMAL_BUILD_MODULE
13+
#include <cstdint>
14+
#endif
15+
16+
namespace boost {
17+
namespace decimal {
18+
namespace detail {
19+
20+
template <typename ReturnType, typename T1, typename T2>
21+
constexpr auto mul_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign,
22+
T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> ReturnType
23+
{
24+
#ifdef BOOST_DECIMAL_DEBUG
25+
std::cerr << "sig lhs: " << sig_lhs
26+
<< "\nexp lhs: " << exp_lhs
27+
<< "\nsig rhs: " << sig_rhs
28+
<< "\nexp rhs: " << exp_rhs;
29+
#endif
30+
31+
bool sign {lhs_sign != rhs_sign};
32+
33+
// Once we have the normalized significands and exponents all we have to do is
34+
// multiply the significands and add the exponents
35+
//
36+
// We use a 64 bit resultant significand because the two 23-bit unsigned significands will always fit
37+
38+
auto res_sig {static_cast<std::uint64_t>(lhs_sig) * static_cast<std::uint64_t>(rhs_sig)};
39+
auto res_exp {lhs_exp + rhs_exp};
40+
41+
const auto sig_dig {detail::num_digits(res_sig)};
42+
43+
if (sig_dig > 9)
44+
{
45+
res_sig /= detail::pow10(static_cast<std::uint64_t>(sig_dig - 9));
46+
res_exp += sig_dig - 9;
47+
}
48+
49+
const auto res_sig_32 {static_cast<typename ReturnType::sig_type>(res_sig)};
50+
51+
#ifdef BOOST_DECIMAL_DEBUG
52+
std::cerr << "\nres sig: " << res_sig_32
53+
<< "\nres exp: " << res_exp << std::endl;
54+
#endif
55+
56+
if (res_sig_32 == 0)
57+
{
58+
sign = false;
59+
}
60+
61+
return {res_sig_32, res_exp, sign};
62+
}
63+
64+
} // namespace detail
65+
} // namespace decimal
66+
} // namespace boost
67+
68+
#endif //BOOST_DECIMAL_DETAIL_MUL_IMPL_HPP
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2023 - 2024 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_DECIMAL_DETAIL_SUB_IMPL_HPP
6+
#define BOOST_DECIMAL_DETAIL_SUB_IMPL_HPP
7+
8+
#include <boost/decimal/detail/shrink_significand.hpp>
9+
#include <boost/decimal/detail/apply_sign.hpp>
10+
#include <boost/decimal/detail/fenv_rounding.hpp>
11+
12+
#ifndef BOOST_DECIMAL_BUILD_MODULE
13+
#include <cstdint>
14+
#endif
15+
16+
namespace boost {
17+
namespace decimal {
18+
namespace detail {
19+
20+
template <typename ReturnType, typename T1, typename T2>
21+
constexpr auto sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign,
22+
T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign,
23+
bool abs_lhs_bigger) noexcept -> ReturnType
24+
{
25+
auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp};
26+
auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)};
27+
auto signed_sig_rhs {detail::make_signed_value(rhs_sig, rhs_sign)};
28+
29+
if (delta_exp > detail::precision + 1)
30+
{
31+
// If the difference in exponents is more than the digits of accuracy
32+
// we return the larger of the two
33+
//
34+
// e.g. 1e20 - 1e-20 = 1e20
35+
return abs_lhs_bigger ? ReturnType{detail::shrink_significand(lhs_sig, lhs_exp), lhs_exp, false} :
36+
ReturnType{detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, true};
37+
}
38+
39+
// The two numbers can be subtracted together without special handling
40+
41+
auto& sig_bigger {abs_lhs_bigger ? signed_sig_lhs : signed_sig_rhs};
42+
auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp};
43+
auto& sig_smaller {abs_lhs_bigger ? signed_sig_rhs : signed_sig_lhs};
44+
auto& smaller_sign {abs_lhs_bigger ? rhs_sign : lhs_sign};
45+
46+
if (delta_exp == 1)
47+
{
48+
sig_bigger *= 10;
49+
--delta_exp;
50+
--exp_bigger;
51+
}
52+
else if (delta_exp >= 2)
53+
{
54+
sig_bigger *= 100;
55+
delta_exp -= 2;
56+
exp_bigger -= 2;
57+
}
58+
59+
if (delta_exp > 1)
60+
{
61+
sig_smaller /= pow10(delta_exp - 1);
62+
delta_exp = 1;
63+
}
64+
65+
if (delta_exp == 1)
66+
{
67+
detail::fenv_round(sig_smaller, smaller_sign);
68+
}
69+
70+
// Both of the significands are less than 9'999'999, so we can safely
71+
// cast them to signed 32-bit ints to calculate the new significand
72+
std::int32_t new_sig = (rhs_sign && !lhs_sign) ?
73+
static_cast<std::int32_t>(signed_sig_lhs) + static_cast<std::int32_t>(signed_sig_rhs) :
74+
static_cast<std::int32_t>(signed_sig_lhs) - static_cast<std::int32_t>(signed_sig_rhs);
75+
76+
const auto new_exp {abs_lhs_bigger ? lhs_exp : rhs_exp};
77+
const auto new_sign {new_sig < 0};
78+
const auto res_sig {detail::make_positive_unsigned(new_sig)};
79+
80+
return {res_sig, new_exp, new_sign};
81+
}
82+
83+
}
84+
}
85+
}
86+
87+
#endif //BOOST_DECIMAL_DETAIL_SUB_IMPL_HPP

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ run link_1.cpp link_2.cpp link_3.cpp ;
5252
run quick.cpp ;
5353
run random_decimal32_comp.cpp ;
5454
run random_decimal32_fast_comp.cpp ;
55+
run random_decimal32_fast_math.cpp ;
5556
run random_decimal32_math.cpp ;
5657
run random_decimal64_comp.cpp ;
5758
run random_decimal64_math.cpp ;

0 commit comments

Comments
 (0)