1// boost\math\tools\promotion.hpp
2
3// Copyright John Maddock 2006.
4// Copyright Paul A. Bristow 2006.
5// Copyright Matt Borland 2023.
6
7// Use, modification and distribution are subject to the
8// Boost Software License, Version 1.0.
9// (See accompanying file LICENSE_1_0.txt
10// or copy at http://www.boost.org/LICENSE_1_0.txt)
11
12// Promote arguments functions to allow math functions to have arguments
13// provided as integer OR real (floating-point, built-in or UDT)
14// (called ArithmeticType in functions that use promotion)
15// that help to reduce the risk of creating multiple instantiations.
16// Allows creation of an inline wrapper that forwards to a foo(RT, RT) function,
17// so you never get to instantiate any mixed foo(RT, IT) functions.
18
19#ifndef BOOST_MATH_PROMOTION_HPP
20#define BOOST_MATH_PROMOTION_HPP
21
22#ifdef _MSC_VER
23#pragma once
24#endif
25
26#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
36
37namespace boost
38{
39 namespace math
40 {
41 namespace tools
42 {
43 // If either T1 or T2 is an integer type,
44 // pretend it was a double (for the purposes of further analysis).
45 // Then pick the wider of the two floating-point types
46 // as the actual signature to forward to.
47 // 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
60 // This follows the C-compatible conversion rules of pow, etc
61 // where pow(int, float) is converted to pow(double, double).
62
63 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;
67 };
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
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
91 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
231
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
242 template <> struct promote_args_2<std::float16_t, std::float16_t> { using type = std::float16_t; };
243 #endif
244
245 template <typename T, typename U>
246 using promote_args_2_t = typename promote_args_2<T, U>::type;
247
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
267#if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
268 //
269 // Guard against use of long double if it's not supported:
270 //
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.");
272#endif
273 };
274
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;
305
306 } // namespace tools
307 } // namespace math
308} // namespace boost
309
310#endif // BOOST_MATH_PROMOTION_HPP
311
312

source code of boost/libs/math/include/boost/math/tools/promotion.hpp