|
3 | 3 | // Copyright John Maddock 2006. |
4 | 4 | // Copyright Paul A. Bristow 2006. |
5 | 5 | // Copyright Matt Borland 2023. |
| 6 | +// Copyright Ryan Elandt 2023. |
6 | 7 |
|
7 | 8 | // Use, modification and distribution are subject to the |
8 | 9 | // Boost Software License, Version 1.0. |
|
24 | 25 | #endif |
25 | 26 |
|
26 | 27 | #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> |
36 | 29 |
|
37 | 30 | namespace boost |
38 | 31 | { |
39 | 32 | namespace math |
40 | 33 | { |
41 | 34 | namespace tools |
42 | 35 | { |
| 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 | + // |
43 | 52 | // If either T1 or T2 is an integer type, |
44 | 53 | // pretend it was a double (for the purposes of further analysis). |
45 | 54 | // Then pick the wider of the two floating-point types |
46 | 55 | // as the actual signature to forward to. |
47 | 56 | // 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 | + // |
60 | 68 | // This follows the C-compatible conversion rules of pow, etc |
61 | 69 | // where pow(int, float) is converted to pow(double, double). |
62 | 70 |
|
| 71 | + |
| 72 | + // Promotes a single argument to double if it is an integer type |
63 | 73 | 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; |
67 | 76 | }; |
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; }; |
74 | 77 |
|
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; |
90 | 78 |
|
| 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 |
91 | 88 | 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 | + }; |
231 | 92 |
|
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; }; |
241 | 93 |
|
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 | + }; |
244 | 110 |
|
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; |
247 | 113 |
|
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; |
266 | 114 |
|
| 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; |
267 | 120 | #if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) |
268 | 121 | // |
269 | 122 | // Guard against use of long double if it's not supported: |
270 | 123 | // |
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."); |
272 | 125 | #endif |
273 | 126 | }; |
274 | 127 |
|
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; |
305 | 130 |
|
306 | 131 | } // namespace tools |
307 | 132 | } // namespace math |
308 | 133 | } // namespace boost |
309 | 134 |
|
310 | 135 | #endif // BOOST_MATH_PROMOTION_HPP |
311 | | - |
0 commit comments