Skip to content

Commit 865eacb

Browse files
committed
Apply promotion changes from boostorg/math#1022
1 parent da6abf9 commit 865eacb

File tree

1 file changed

+71
-247
lines changed

1 file changed

+71
-247
lines changed

include/boost/math/tools/promotion.hpp

Lines changed: 71 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Copyright John Maddock 2006.
44
// Copyright Paul A. Bristow 2006.
55
// Copyright Matt Borland 2023.
6+
// Copyright Ryan Elandt 2023.
67

78
// Use, modification and distribution are subject to the
89
// Boost Software License, Version 1.0.
@@ -24,288 +25,111 @@
2425
#endif
2526

2627
#include <boost/math/tools/config.hpp>
27-
#include <type_traits>
28-
29-
#if defined __has_include
30-
# if __cplusplus > 202002L || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
31-
# if __has_include (<stdfloat>)
32-
# include <stdfloat>
33-
# endif
34-
# endif
35-
#endif
28+
#include <boost/math/tools/type_traits.hpp>
3629

3730
namespace boost
3831
{
3932
namespace math
4033
{
4134
namespace tools
4235
{
36+
///// This promotion system works as follows:
37+
//
38+
// Rule<T1> (one argument promotion rule):
39+
// - Promotes `T` to `double` if `T` is an integer type as identified by
40+
// `std::is_integral`, otherwise is `T`
41+
//
42+
// Rule<T1, T2_to_TN...> (two or more argument promotion rule):
43+
// - 1. Calculates type using applying Rule<T1>.
44+
// - 2. Calculates type using applying Rule<T2_to_TN...>
45+
// - If the type calculated in 1 and 2 are both floating point types, as
46+
// identified by `std::is_floating_point`, then return the type
47+
// determined by `std::common_type`. Otherwise return the type using
48+
// an asymmetric convertibility rule.
49+
//
50+
///// Discussion:
51+
//
4352
// If either T1 or T2 is an integer type,
4453
// pretend it was a double (for the purposes of further analysis).
4554
// Then pick the wider of the two floating-point types
4655
// as the actual signature to forward to.
4756
// For example:
48-
// foo(int, short) -> double foo(double, double);
49-
// foo(int, float) -> double foo(double, double);
50-
// Note: NOT float foo(float, float)
51-
// foo(int, double) -> foo(double, double);
52-
// foo(double, float) -> double foo(double, double);
53-
// foo(double, float) -> double foo(double, double);
54-
// foo(any-int-or-float-type, long double) -> foo(long double, long double);
55-
// but ONLY float foo(float, float) is unchanged.
56-
// So the only way to get an entirely float version is to call foo(1.F, 2.F),
57-
// But since most (all?) the math functions convert to double internally,
58-
// probably there would not be the hoped-for gain by using float here.
59-
57+
// foo(int, short) -> double foo(double, double); // ***NOT*** float foo(float, float)
58+
// foo(int, float) -> double foo(double, double); // ***NOT*** float foo(float, float)
59+
// foo(int, double) -> foo(double, double);
60+
// foo(double, float) -> double foo(double, double);
61+
// foo(double, float) -> double foo(double, double);
62+
// foo(any-int-or-float-type, long double) -> foo(long double, long double);
63+
// ONLY float foo(float, float) is unchanged, so the only way to get an
64+
// entirely float version is to call foo(1.F, 2.F). But since most (all?) the
65+
// math functions convert to double internally, probably there would not be the
66+
// hoped-for gain by using float here.
67+
//
6068
// This follows the C-compatible conversion rules of pow, etc
6169
// where pow(int, float) is converted to pow(double, double).
6270

71+
72+
// Promotes a single argument to double if it is an integer type
6373
template <class T>
64-
struct promote_arg
65-
{ // If T is integral type, then promote to double.
66-
using type = typename std::conditional<std::is_integral<T>::value, double, T>::type;
74+
struct promote_arg {
75+
using type = typename boost::math::conditional<boost::math::is_integral<T>::value, double, T>::type;
6776
};
68-
// These full specialisations reduce std::conditional usage and speed up
69-
// compilation:
70-
template <> struct promote_arg<float> { using type = float; };
71-
template <> struct promote_arg<double>{ using type = double; };
72-
template <> struct promote_arg<long double> { using type = long double; };
73-
template <> struct promote_arg<int> { using type = double; };
7477

75-
#ifdef __STDCPP_FLOAT16_T__
76-
template <> struct promote_arg<std::float16_t> { using type = std::float16_t; };
77-
#endif
78-
#ifdef __STDCPP_FLOAT32_T__
79-
template <> struct promote_arg<std::float32_t> { using type = std::float32_t; };
80-
#endif
81-
#ifdef __STDCPP_FLOAT64_T__
82-
template <> struct promote_arg<std::float64_t> { using type = std::float64_t; };
83-
#endif
84-
#ifdef __STDCPP_FLOAT128_T__
85-
template <> struct promote_arg<std::float128_t> { using type = std::float128_t; };
86-
#endif
87-
88-
template <typename T>
89-
using promote_arg_t = typename promote_arg<T>::type;
9078

79+
// Promotes two arguments, neither of which is an integer type using an asymmetric
80+
// convertibility rule.
81+
template <class T1, class T2, bool = (boost::math::is_floating_point<T1>::value && boost::math::is_floating_point<T2>::value)>
82+
struct pa2_integral_already_removed {
83+
using type = typename boost::math::conditional<
84+
!boost::math::is_floating_point<T2>::value && boost::math::is_convertible<T1, T2>::value,
85+
T2, T1>::type;
86+
};
87+
// For two floating point types, promotes using `std::common_type` functionality
9188
template <class T1, class T2>
92-
struct promote_args_2
93-
{ // Promote, if necessary, & pick the wider of the two floating-point types.
94-
// for both parameter types, if integral promote to double.
95-
using T1P = typename promote_arg<T1>::type; // T1 perhaps promoted.
96-
using T2P = typename promote_arg<T2>::type; // T2 perhaps promoted.
97-
using intermediate_type = typename std::conditional<
98-
std::is_floating_point<T1P>::value && std::is_floating_point<T2P>::value, // both T1P and T2P are floating-point?
99-
#ifdef __STDCPP_FLOAT128_T__
100-
typename std::conditional<std::is_same<std::float128_t, T1P>::value || std::is_same<std::float128_t, T2P>::value, // either long double?
101-
std::float128_t,
102-
#endif
103-
#ifdef BOOST_MATH_USE_FLOAT128
104-
typename std::conditional<std::is_same<__float128, T1P>::value || std::is_same<__float128, T2P>::value, // either long double?
105-
__float128,
106-
#endif
107-
typename std::conditional<std::is_same<long double, T1P>::value || std::is_same<long double, T2P>::value, // either long double?
108-
long double, // then result type is long double.
109-
#ifdef __STDCPP_FLOAT64_T__
110-
typename std::conditional<std::is_same<std::float64_t, T1P>::value || std::is_same<std::float64_t, T2P>::value, // either float64?
111-
std::float64_t, // then result type is float64_t.
112-
#endif
113-
typename std::conditional<std::is_same<double, T1P>::value || std::is_same<double, T2P>::value, // either double?
114-
double, // result type is double.
115-
#ifdef __STDCPP_FLOAT32_T__
116-
typename std::conditional<std::is_same<std::float32_t, T1P>::value || std::is_same<std::float32_t, T2P>::value, // either float32?
117-
std::float32_t, // then result type is float32_t.
118-
#endif
119-
float // else result type is float.
120-
>::type
121-
#ifdef BOOST_MATH_USE_FLOAT128
122-
>::type
123-
#endif
124-
#ifdef __STDCPP_FLOAT128_T__
125-
>::type
126-
#endif
127-
#ifdef __STDCPP_FLOAT64_T__
128-
>::type
129-
#endif
130-
#ifdef __STDCPP_FLOAT32_T__
131-
>::type
132-
#endif
133-
>::type,
134-
// else one or the other is a user-defined type:
135-
typename std::conditional<!std::is_floating_point<T2P>::value && std::is_convertible<T1P, T2P>::value, T2P, T1P>::type>::type;
136-
137-
#ifdef __STDCPP_FLOAT64_T__
138-
// If long doubles are doubles then we should prefer to use std::float64_t when available
139-
using type = std::conditional_t<(sizeof(double) == sizeof(long double) && std::is_same<intermediate_type, long double>::value), std::float64_t, intermediate_type>;
140-
#else
141-
using type = intermediate_type;
142-
#endif
143-
}; // promote_arg2
144-
// These full specialisations reduce std::conditional usage and speed up
145-
// compilation:
146-
template <> struct promote_args_2<float, float> { using type = float; };
147-
template <> struct promote_args_2<double, double>{ using type = double; };
148-
template <> struct promote_args_2<long double, long double> { using type = long double; };
149-
template <> struct promote_args_2<int, int> { using type = double; };
150-
template <> struct promote_args_2<int, float> { using type = double; };
151-
template <> struct promote_args_2<float, int> { using type = double; };
152-
template <> struct promote_args_2<int, double> { using type = double; };
153-
template <> struct promote_args_2<double, int> { using type = double; };
154-
template <> struct promote_args_2<int, long double> { using type = long double; };
155-
template <> struct promote_args_2<long double, int> { using type = long double; };
156-
template <> struct promote_args_2<float, double> { using type = double; };
157-
template <> struct promote_args_2<double, float> { using type = double; };
158-
template <> struct promote_args_2<float, long double> { using type = long double; };
159-
template <> struct promote_args_2<long double, float> { using type = long double; };
160-
template <> struct promote_args_2<double, long double> { using type = long double; };
161-
template <> struct promote_args_2<long double, double> { using type = long double; };
162-
163-
#ifdef __STDCPP_FLOAT128_T__
164-
template <> struct promote_args_2<int, std::float128_t> { using type = std::float128_t; };
165-
template <> struct promote_args_2<std::float128_t, int> { using type = std::float128_t; };
166-
template <> struct promote_args_2<std::float128_t, float> { using type = std::float128_t; };
167-
template <> struct promote_args_2<float, std::float128_t> { using type = std::float128_t; };
168-
template <> struct promote_args_2<std::float128_t, double> { using type = std::float128_t; };
169-
template <> struct promote_args_2<double, std::float128_t> { using type = std::float128_t; };
170-
template <> struct promote_args_2<std::float128_t, long double> { using type = std::float128_t; };
171-
template <> struct promote_args_2<long double, std::float128_t> { using type = std::float128_t; };
172-
173-
#ifdef __STDCPP_FLOAT16_T__
174-
template <> struct promote_args_2<std::float128_t, std::float16_t> { using type = std::float128_t; };
175-
template <> struct promote_args_2<std::float16_t, std::float128_t> { using type = std::float128_t; };
176-
#endif
177-
178-
#ifdef __STDCPP_FLOAT32_T__
179-
template <> struct promote_args_2<std::float128_t, std::float32_t> { using type = std::float128_t; };
180-
template <> struct promote_args_2<std::float32_t, std::float128_t> { using type = std::float128_t; };
181-
#endif
182-
183-
#ifdef __STDCPP_FLOAT64_T__
184-
template <> struct promote_args_2<std::float128_t, std::float64_t> { using type = std::float128_t; };
185-
template <> struct promote_args_2<std::float64_t, std::float128_t> { using type = std::float128_t; };
186-
#endif
187-
188-
template <> struct promote_args_2<std::float128_t, std::float128_t> { using type = std::float128_t; };
189-
#endif
190-
191-
#ifdef __STDCPP_FLOAT64_T__
192-
template <> struct promote_args_2<int, std::float64_t> { using type = std::float64_t; };
193-
template <> struct promote_args_2<std::float64_t, int> { using type = std::float64_t; };
194-
template <> struct promote_args_2<std::float64_t, float> { using type = std::float64_t; };
195-
template <> struct promote_args_2<float, std::float64_t> { using type = std::float64_t; };
196-
template <> struct promote_args_2<std::float64_t, double> { using type = std::float64_t; };
197-
template <> struct promote_args_2<double, std::float64_t> { using type = std::float64_t; };
198-
template <> struct promote_args_2<std::float64_t, long double> { using type = long double; };
199-
template <> struct promote_args_2<long double, std::float64_t> { using type = long double; };
200-
201-
#ifdef __STDCPP_FLOAT16_T__
202-
template <> struct promote_args_2<std::float64_t, std::float16_t> { using type = std::float64_t; };
203-
template <> struct promote_args_2<std::float16_t, std::float64_t> { using type = std::float64_t; };
204-
#endif
205-
206-
#ifdef __STDCPP_FLOAT32_T__
207-
template <> struct promote_args_2<std::float64_t, std::float32_t> { using type = std::float64_t; };
208-
template <> struct promote_args_2<std::float32_t, std::float64_t> { using type = std::float64_t; };
209-
#endif
210-
211-
template <> struct promote_args_2<std::float64_t, std::float64_t> { using type = std::float64_t; };
212-
#endif
213-
214-
#ifdef __STDCPP_FLOAT32_T__
215-
template <> struct promote_args_2<int, std::float32_t> { using type = std::float32_t; };
216-
template <> struct promote_args_2<std::float32_t, int> { using type = std::float32_t; };
217-
template <> struct promote_args_2<std::float32_t, float> { using type = std::float32_t; };
218-
template <> struct promote_args_2<float, std::float32_t> { using type = std::float32_t; };
219-
template <> struct promote_args_2<std::float32_t, double> { using type = double; };
220-
template <> struct promote_args_2<double, std::float32_t> { using type = double; };
221-
template <> struct promote_args_2<std::float32_t, long double> { using type = long double; };
222-
template <> struct promote_args_2<long double, std::float32_t> { using type = long double; };
223-
224-
#ifdef __STDCPP_FLOAT16_T__
225-
template <> struct promote_args_2<std::float32_t, std::float16_t> { using type = std::float32_t; };
226-
template <> struct promote_args_2<std::float16_t, std::float32_t> { using type = std::float32_t; };
227-
#endif
228-
229-
template <> struct promote_args_2<std::float32_t, std::float32_t> { using type = std::float32_t; };
230-
#endif
89+
struct pa2_integral_already_removed<T1, T2, true> {
90+
using type = boost::math::common_type_t<T1, T2, float>;
91+
};
23192

232-
#ifdef __STDCPP_FLOAT16_T__
233-
template <> struct promote_args_2<int, std::float16_t> { using type = std::float16_t; };
234-
template <> struct promote_args_2<std::float16_t, int> { using type = std::float16_t; };
235-
template <> struct promote_args_2<std::float16_t, float> { using type = float; };
236-
template <> struct promote_args_2<float, std::float16_t> { using type = float; };
237-
template <> struct promote_args_2<std::float16_t, double> { using type = double; };
238-
template <> struct promote_args_2<double, std::float16_t> { using type = double; };
239-
template <> struct promote_args_2<std::float16_t, long double> { using type = long double; };
240-
template <> struct promote_args_2<long double, std::float16_t> { using type = long double; };
24193

242-
template <> struct promote_args_2<std::float16_t, std::float16_t> { using type = std::float16_t; };
243-
#endif
94+
// Template definition for promote_args_permissive
95+
template <typename... Args>
96+
struct promote_args_permissive;
97+
// Specialization for one argument
98+
template <typename T>
99+
struct promote_args_permissive<T> {
100+
using type = typename promote_arg<typename boost::math::remove_cv<T>::type>::type;
101+
};
102+
// Specialization for two or more arguments
103+
template <typename T1, typename... T2_to_TN>
104+
struct promote_args_permissive<T1, T2_to_TN...> {
105+
using type = typename pa2_integral_already_removed<
106+
typename promote_args_permissive<T1>::type,
107+
typename promote_args_permissive<T2_to_TN...>::type
108+
>::type;
109+
};
244110

245-
template <typename T, typename U>
246-
using promote_args_2_t = typename promote_args_2<T, U>::type;
111+
template <class... Args>
112+
using promote_args_permissive_t = typename promote_args_permissive<Args...>::type;
247113

248-
template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
249-
struct promote_args
250-
{
251-
using type = typename promote_args_2<
252-
typename std::remove_cv<T1>::type,
253-
typename promote_args_2<
254-
typename std::remove_cv<T2>::type,
255-
typename promote_args_2<
256-
typename std::remove_cv<T3>::type,
257-
typename promote_args_2<
258-
typename std::remove_cv<T4>::type,
259-
typename promote_args_2<
260-
typename std::remove_cv<T5>::type, typename std::remove_cv<T6>::type
261-
>::type
262-
>::type
263-
>::type
264-
>::type
265-
>::type;
266114

115+
// Same as `promote_args_permissive` but with a static assertion that the promoted type
116+
// is not `long double` if `BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS` is defined
117+
template <class... Args>
118+
struct promote_args {
119+
using type = typename promote_args_permissive<Args...>::type;
267120
#if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
268121
//
269122
// Guard against use of long double if it's not supported:
270123
//
271-
static_assert((0 == std::is_same<type, long double>::value), "Sorry, but this platform does not have sufficient long double support for the special functions to be reliably implemented.");
124+
static_assert((0 == boost::math::is_same<type, long double>::value), "Sorry, but this platform does not have sufficient long double support for the special functions to be reliably implemented.");
272125
#endif
273126
};
274127

275-
template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
276-
using promote_args_t = typename promote_args<T1, T2, T3, T4, T5, T6>::type;
277-
278-
//
279-
// This struct is the same as above, but has no static assert on long double usage,
280-
// it should be used only on functions that can be implemented for long double
281-
// even when std lib support is missing or broken for that type.
282-
//
283-
template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
284-
struct promote_args_permissive
285-
{
286-
using type = typename promote_args_2<
287-
typename std::remove_cv<T1>::type,
288-
typename promote_args_2<
289-
typename std::remove_cv<T2>::type,
290-
typename promote_args_2<
291-
typename std::remove_cv<T3>::type,
292-
typename promote_args_2<
293-
typename std::remove_cv<T4>::type,
294-
typename promote_args_2<
295-
typename std::remove_cv<T5>::type, typename std::remove_cv<T6>::type
296-
>::type
297-
>::type
298-
>::type
299-
>::type
300-
>::type;
301-
};
302-
303-
template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
304-
using promote_args_permissive_t = typename promote_args_permissive<T1, T2, T3, T4, T5, T6>::type;
128+
template <class... Args>
129+
using promote_args_t = typename promote_args<Args...>::type;
305130

306131
} // namespace tools
307132
} // namespace math
308133
} // namespace boost
309134

310135
#endif // BOOST_MATH_PROMOTION_HPP
311-

0 commit comments

Comments
 (0)