Skip to content

Commit ca9fb0a

Browse files
authored
Merge pull request #770 from cppalliance/32fma
Add 32-bit FMA implementation
2 parents 90f0469 + 30c0fba commit ca9fb0a

File tree

6 files changed

+136
-75
lines changed

6 files changed

+136
-75
lines changed

include/boost/decimal/decimal32.hpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <boost/decimal/detail/mul_impl.hpp>
3838
#include <boost/decimal/detail/div_impl.hpp>
3939
#include <boost/decimal/detail/promote_significand.hpp>
40+
#include <boost/decimal/detail/components.hpp>
4041

4142
#ifndef BOOST_DECIMAL_BUILD_MODULE
4243

@@ -121,16 +122,6 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t d32_big_combination_field_mask =
121122
//BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t d32_construct_exp_mask = UINT32_C(0b0'00000'111111'0000000000'0000000000);
122123
//BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint32_t d32_construct_significand_mask = d32_no_combination;
123124

124-
struct decimal32_components
125-
{
126-
using significand_type = std::uint32_t;
127-
using biased_exponent_type = std::int32_t;
128-
129-
significand_type sig;
130-
biased_exponent_type exp;
131-
bool sign;
132-
};
133-
134125
} // namespace detail
135126

136127
#if defined(__GNUC__) && __GNUC__ >= 8

include/boost/decimal/decimal32_fast.hpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d32_fast_inf = std::numeric_limits<std::ui
3131
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d32_fast_qnan = std::numeric_limits<std::uint_fast32_t>::max() - 1;
3232
BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d32_fast_snan = std::numeric_limits<std::uint_fast32_t>::max() - 2;
3333

34-
struct decimal32_fast_components
35-
{
36-
using significand_type = std::uint_fast32_t;
37-
using biased_exponent_type = std::int_fast32_t;
38-
39-
significand_type sig;
40-
biased_exponent_type exp;
41-
bool sign;
42-
};
43-
4434
}
4535

4636
BOOST_DECIMAL_EXPORT class decimal32_fast final

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

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,98 @@
99
#include <boost/decimal/decimal32_fast.hpp>
1010
#include <boost/decimal/decimal64.hpp>
1111
#include <boost/decimal/decimal128.hpp>
12+
#include <boost/decimal/decimal128_fast.hpp>
1213
#include <boost/decimal/detail/config.hpp>
1314

1415
namespace boost {
1516
namespace decimal {
1617

18+
namespace detail {
19+
20+
#ifdef _MSC_VER
21+
#pragma warning(push)
22+
#pragma warning(disable : 4127)
23+
#endif
24+
25+
template <BOOST_DECIMAL_DECIMAL_FLOATING_TYPE Dec>
26+
using components_type = std::conditional_t<std::is_same<Dec, decimal32>::value, decimal32_components,
27+
std::conditional_t<std::is_same<Dec, decimal32_fast>::value, decimal32_fast_components,
28+
std::conditional_t<std::is_same<Dec, decimal64>::value, decimal64_components,
29+
std::conditional_t<std::is_same<Dec, decimal64_fast>::value, decimal64_fast_components,
30+
std::conditional_t<std::is_same<Dec, decimal128>::value, decimal128_components, decimal128_fast_components
31+
>>>>>;
32+
33+
template <bool checked, BOOST_DECIMAL_DECIMAL_FLOATING_TYPE T>
34+
constexpr auto d32_fma_impl(T x, T y, T z) noexcept -> T
35+
{
36+
using T_components_type = components_type<T>;
37+
using exp_type = typename T::biased_exponent_type;
38+
39+
// Apply the add
40+
#ifndef BOOST_DECIMAL_FAST_MATH
41+
BOOST_DECIMAL_IF_CONSTEXPR (checked)
42+
{
43+
if (!isfinite(x) || !isfinite(y))
44+
{
45+
return detail::check_non_finite(x, y);
46+
}
47+
}
48+
#endif
49+
50+
int exp_lhs {};
51+
auto sig_lhs = frexp10(x, &exp_lhs);
52+
53+
int exp_rhs {};
54+
auto sig_rhs = frexp10(y, &exp_rhs);
55+
56+
auto first_res = detail::mul_impl<T_components_type>(sig_lhs, static_cast<exp_type>(exp_lhs), x < 0,
57+
sig_rhs, static_cast<exp_type>(exp_rhs), y < 0);
58+
59+
// Apply the mul on the carried components
60+
// We still create the result as a decimal type to check for non-finite values and comparisons,
61+
// but we do not use it for the resultant calculation
62+
const T complete_lhs {first_res.sig, first_res.exp, first_res.sign};
63+
64+
#ifndef BOOST_DECIMAL_FAST_MATH
65+
BOOST_DECIMAL_IF_CONSTEXPR (checked)
66+
{
67+
if (!isfinite(complete_lhs) || !isfinite(z))
68+
{
69+
return detail::check_non_finite(complete_lhs, z);
70+
}
71+
}
72+
#endif
73+
74+
const bool abs_lhs_bigger {abs(complete_lhs) > abs(z)};
75+
76+
int exp_z {};
77+
auto sig_z = frexp10(z, &exp_z);
78+
detail::normalize<T>(first_res.sig, first_res.exp);
79+
80+
return detail::d32_add_impl<T>(first_res.sig, first_res.exp, first_res.sign,
81+
sig_z, static_cast<exp_type>(exp_z), z < 0,
82+
abs_lhs_bigger);
83+
}
84+
85+
#ifdef _MSC_VER
86+
#pragma warning(pop)
87+
#endif
88+
89+
constexpr auto unchecked_fma(decimal32 x, decimal32 y, decimal32 z) noexcept -> decimal32
90+
{
91+
return detail::d32_fma_impl<false>(x, y, z);
92+
}
93+
94+
constexpr auto unchecked_fma(decimal32_fast x, decimal32_fast y, decimal32_fast z) noexcept -> decimal32_fast
95+
{
96+
return detail::d32_fma_impl<false>(x, y, z);
97+
}
98+
99+
} // Namespace detail
100+
17101
BOOST_DECIMAL_EXPORT constexpr auto fma(decimal32 x, decimal32 y, decimal32 z) noexcept -> decimal32
18102
{
19-
return x * y + z;
103+
return detail::d32_fma_impl<true>(x, y, z);
20104
}
21105

22106
BOOST_DECIMAL_EXPORT constexpr auto fma(decimal64 x, decimal64 y, decimal64 z) noexcept -> decimal64
@@ -31,7 +115,7 @@ BOOST_DECIMAL_EXPORT constexpr auto fma(decimal128 x, decimal128 y, decimal128 z
31115

32116
BOOST_DECIMAL_EXPORT constexpr auto fma(decimal32_fast x, decimal32_fast y, decimal32_fast z) noexcept -> decimal32_fast
33117
{
34-
return x * y + z;
118+
return detail::d32_fma_impl<true>(x, y, z);
35119
}
36120

37121
BOOST_DECIMAL_EXPORT constexpr auto fma(decimal64_fast x, decimal64_fast y, decimal64_fast z) noexcept -> decimal64_fast
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 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_COMPONENTS_HPP
6+
#define BOOST_DECIMAL_DETAIL_COMPONENTS_HPP
7+
8+
#include <boost/decimal/detail/config.hpp>
9+
10+
#ifndef BOOST_DECIMAL_BUILD_MODULE
11+
#include <cstdint>
12+
#endif
13+
14+
namespace boost {
15+
namespace decimal {
16+
namespace detail {
17+
18+
struct decimal32_components
19+
{
20+
using significand_type = std::uint32_t;
21+
using biased_exponent_type = std::int32_t;
22+
23+
significand_type sig;
24+
biased_exponent_type exp;
25+
bool sign;
26+
};
27+
28+
struct decimal32_fast_components
29+
{
30+
using significand_type = std::uint_fast32_t;
31+
using biased_exponent_type = std::int_fast32_t;
32+
33+
significand_type sig;
34+
biased_exponent_type exp;
35+
bool sign;
36+
};
37+
38+
} // namespace detail
39+
} // namespace decimal
40+
} // namespace boost
41+
42+
#endif // BOOST_DECIMAL_DETAIL_COMPONENTS_HPP

include/boost/decimal/detail/mul_impl.hpp

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <boost/decimal/detail/emulated128.hpp>
1212
#include <boost/decimal/detail/emulated256.hpp>
1313
#include <boost/decimal/detail/power_tables.hpp>
14+
#include <boost/decimal/detail/components.hpp>
1415

1516
#ifndef BOOST_DECIMAL_BUILD_MODULE
1617
#include <cstdint>
@@ -24,21 +25,10 @@ namespace detail {
2425
// 1) Returns a decimal type and lets the constructor handle with shrinking the significand
2526
// 2) Returns a struct of the constituent components (used with FMAs)
2627

27-
template <typename ReturnType, typename T, typename U>
28-
BOOST_DECIMAL_FORCE_INLINE constexpr auto mul_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
29-
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<ReturnType, decimal32_fast>::value, ReturnType>
30-
{
31-
using mul_type = std::uint_fast64_t;
32-
33-
const auto res_sig {static_cast<mul_type>(lhs_sig) * static_cast<mul_type>(rhs_sig)};
34-
const auto res_exp {lhs_exp + rhs_exp};
35-
36-
return {res_sig, res_exp, lhs_sign != rhs_sign};
37-
}
3828

3929
template <typename ReturnType, typename T, typename U>
4030
BOOST_DECIMAL_FORCE_INLINE constexpr auto mul_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
41-
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<std::is_same<ReturnType, decimal32>::value, ReturnType>
31+
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> ReturnType
4232
{
4333
using mul_type = std::uint_fast64_t;
4434

@@ -54,47 +44,6 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto mul_impl(T lhs_sig, U lhs_exp, bool lh
5444
return {static_cast<std::uint32_t>(res_sig), res_exp, lhs_sign != rhs_sign};
5545
}
5646

57-
template <typename ReturnType, typename T, typename U>
58-
BOOST_DECIMAL_FORCE_INLINE constexpr auto mul_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
59-
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept -> std::enable_if_t<!detail::is_decimal_floating_point_v<ReturnType>, ReturnType>
60-
{
61-
using mul_type = std::uint_fast64_t;
62-
63-
#ifdef BOOST_DECIMAL_DEBUG
64-
std::cerr << "sig lhs: " << sig_lhs
65-
<< "\nexp lhs: " << exp_lhs
66-
<< "\nsig rhs: " << sig_rhs
67-
<< "\nexp rhs: " << exp_rhs;
68-
#endif
69-
70-
bool sign {lhs_sign != rhs_sign};
71-
72-
// Once we have the normalized significands and exponents all we have to do is
73-
// multiply the significands and add the exponents
74-
//
75-
// We use a 64 bit resultant significand because the two 23-bit unsigned significands will always fit
76-
77-
auto res_sig {static_cast<mul_type>(lhs_sig) * static_cast<mul_type>(rhs_sig)};
78-
auto res_exp {lhs_exp + rhs_exp};
79-
80-
// We don't need to use the regular binary search tree detail::num_digits(res_sig)
81-
// because we know that res_sig must be [1'000'000^2, 9'999'999^2] which only differ by one order
82-
// of magnitude in their number of digits
83-
const auto sig_dig {res_sig >= UINT64_C(10000000000000) ? 14 : 13};
84-
constexpr auto max_dig {std::numeric_limits<typename ReturnType::significand_type>::digits10};
85-
res_sig /= detail::pow10(static_cast<mul_type>(sig_dig - max_dig));
86-
res_exp += sig_dig - max_dig;
87-
88-
const auto res_sig_32 {static_cast<typename ReturnType::significand_type>(res_sig)};
89-
90-
#ifdef BOOST_DECIMAL_DEBUG
91-
std::cerr << "\nres sig: " << res_sig_32
92-
<< "\nres exp: " << res_exp << std::endl;
93-
#endif
94-
95-
return {res_sig_32, res_exp, sign};
96-
}
97-
9847
template <typename ReturnType, BOOST_DECIMAL_INTEGRAL T, BOOST_DECIMAL_INTEGRAL U>
9948
BOOST_DECIMAL_FORCE_INLINE constexpr auto d64_mul_impl(T lhs_sig, U lhs_exp, bool lhs_sign,
10049
T rhs_sig, U rhs_exp, bool rhs_sign) noexcept

test/test_big_uints.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ int main()
3030
# pragma clang diagnostic ignored "-Wsign-conversion"
3131
# pragma clang diagnostic ignored "-Wfloat-equal"
3232
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
33+
34+
# if __clang_major__ >= 20
35+
# pragma clang diagnostic ignored "-Wdeprecated-literal-operator"
36+
# endif
37+
3338
#elif defined(__GNUC__)
3439
# pragma GCC diagnostic push
3540
# pragma GCC diagnostic ignored "-Wold-style-cast"

0 commit comments

Comments
 (0)