promotion.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. // boost\math\tools\promotion.hpp
  2. // Copyright John Maddock 2006.
  3. // Copyright Paul A. Bristow 2006.
  4. // Copyright Matt Borland 2023.
  5. // Use, modification and distribution are subject to the
  6. // Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt
  8. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. // Promote arguments functions to allow math functions to have arguments
  10. // provided as integer OR real (floating-point, built-in or UDT)
  11. // (called ArithmeticType in functions that use promotion)
  12. // that help to reduce the risk of creating multiple instantiations.
  13. // Allows creation of an inline wrapper that forwards to a foo(RT, RT) function,
  14. // so you never get to instantiate any mixed foo(RT, IT) functions.
  15. #ifndef BOOST_MATH_PROMOTION_HPP
  16. #define BOOST_MATH_PROMOTION_HPP
  17. #ifdef _MSC_VER
  18. #pragma once
  19. #endif
  20. #include <boost/math/tools/config.hpp>
  21. #include <type_traits>
  22. #if defined __has_include
  23. # if __cplusplus > 202002L || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L)
  24. # if __has_include (<stdfloat>)
  25. # include <stdfloat>
  26. # endif
  27. # endif
  28. #endif
  29. namespace boost
  30. {
  31. namespace math
  32. {
  33. namespace tools
  34. {
  35. // If either T1 or T2 is an integer type,
  36. // pretend it was a double (for the purposes of further analysis).
  37. // Then pick the wider of the two floating-point types
  38. // as the actual signature to forward to.
  39. // For example:
  40. // foo(int, short) -> double foo(double, double);
  41. // foo(int, float) -> double foo(double, double);
  42. // Note: NOT float foo(float, float)
  43. // foo(int, double) -> foo(double, double);
  44. // foo(double, float) -> double foo(double, double);
  45. // foo(double, float) -> double foo(double, double);
  46. // foo(any-int-or-float-type, long double) -> foo(long double, long double);
  47. // but ONLY float foo(float, float) is unchanged.
  48. // So the only way to get an entirely float version is to call foo(1.F, 2.F),
  49. // But since most (all?) the math functions convert to double internally,
  50. // probably there would not be the hoped-for gain by using float here.
  51. // This follows the C-compatible conversion rules of pow, etc
  52. // where pow(int, float) is converted to pow(double, double).
  53. template <class T>
  54. struct promote_arg
  55. { // If T is integral type, then promote to double.
  56. using type = typename std::conditional<std::is_integral<T>::value, double, T>::type;
  57. };
  58. // These full specialisations reduce std::conditional usage and speed up
  59. // compilation:
  60. template <> struct promote_arg<float> { using type = float; };
  61. template <> struct promote_arg<double>{ using type = double; };
  62. template <> struct promote_arg<long double> { using type = long double; };
  63. template <> struct promote_arg<int> { using type = double; };
  64. #ifdef __STDCPP_FLOAT16_T__
  65. template <> struct promote_arg<std::float16_t> { using type = std::float16_t; };
  66. #endif
  67. #ifdef __STDCPP_FLOAT32_T__
  68. template <> struct promote_arg<std::float32_t> { using type = std::float32_t; };
  69. #endif
  70. #ifdef __STDCPP_FLOAT64_T__
  71. template <> struct promote_arg<std::float64_t> { using type = std::float64_t; };
  72. #endif
  73. #ifdef __STDCPP_FLOAT128_T__
  74. template <> struct promote_arg<std::float128_t> { using type = std::float128_t; };
  75. #endif
  76. template <typename T>
  77. using promote_arg_t = typename promote_arg<T>::type;
  78. template <class T1, class T2>
  79. struct promote_args_2
  80. { // Promote, if necessary, & pick the wider of the two floating-point types.
  81. // for both parameter types, if integral promote to double.
  82. using T1P = typename promote_arg<T1>::type; // T1 perhaps promoted.
  83. using T2P = typename promote_arg<T2>::type; // T2 perhaps promoted.
  84. using intermediate_type = typename std::conditional<
  85. std::is_floating_point<T1P>::value && std::is_floating_point<T2P>::value, // both T1P and T2P are floating-point?
  86. #ifdef __STDCPP_FLOAT128_T__
  87. typename std::conditional<std::is_same<std::float128_t, T1P>::value || std::is_same<std::float128_t, T2P>::value, // either long double?
  88. std::float128_t,
  89. #endif
  90. #ifdef BOOST_MATH_USE_FLOAT128
  91. typename std::conditional<std::is_same<__float128, T1P>::value || std::is_same<__float128, T2P>::value, // either long double?
  92. __float128,
  93. #endif
  94. typename std::conditional<std::is_same<long double, T1P>::value || std::is_same<long double, T2P>::value, // either long double?
  95. long double, // then result type is long double.
  96. #ifdef __STDCPP_FLOAT64_T__
  97. typename std::conditional<std::is_same<std::float64_t, T1P>::value || std::is_same<std::float64_t, T2P>::value, // either float64?
  98. std::float64_t, // then result type is float64_t.
  99. #endif
  100. typename std::conditional<std::is_same<double, T1P>::value || std::is_same<double, T2P>::value, // either double?
  101. double, // result type is double.
  102. #ifdef __STDCPP_FLOAT32_T__
  103. typename std::conditional<std::is_same<std::float32_t, T1P>::value || std::is_same<std::float32_t, T2P>::value, // either float32?
  104. std::float32_t, // then result type is float32_t.
  105. #endif
  106. float // else result type is float.
  107. >::type
  108. #ifdef BOOST_MATH_USE_FLOAT128
  109. >::type
  110. #endif
  111. #ifdef __STDCPP_FLOAT128_T__
  112. >::type
  113. #endif
  114. #ifdef __STDCPP_FLOAT64_T__
  115. >::type
  116. #endif
  117. #ifdef __STDCPP_FLOAT32_T__
  118. >::type
  119. #endif
  120. >::type,
  121. // else one or the other is a user-defined type:
  122. typename std::conditional<!std::is_floating_point<T2P>::value && std::is_convertible<T1P, T2P>::value, T2P, T1P>::type>::type;
  123. #ifdef __STDCPP_FLOAT64_T__
  124. // If long doubles are doubles then we should prefer to use std::float64_t when available
  125. using type = std::conditional_t<(sizeof(double) == sizeof(long double) && std::is_same<intermediate_type, long double>::value), std::float64_t, intermediate_type>;
  126. #else
  127. using type = intermediate_type;
  128. #endif
  129. }; // promote_arg2
  130. // These full specialisations reduce std::conditional usage and speed up
  131. // compilation:
  132. template <> struct promote_args_2<float, float> { using type = float; };
  133. template <> struct promote_args_2<double, double>{ using type = double; };
  134. template <> struct promote_args_2<long double, long double> { using type = long double; };
  135. template <> struct promote_args_2<int, int> { using type = double; };
  136. template <> struct promote_args_2<int, float> { using type = double; };
  137. template <> struct promote_args_2<float, int> { using type = double; };
  138. template <> struct promote_args_2<int, double> { using type = double; };
  139. template <> struct promote_args_2<double, int> { using type = double; };
  140. template <> struct promote_args_2<int, long double> { using type = long double; };
  141. template <> struct promote_args_2<long double, int> { using type = long double; };
  142. template <> struct promote_args_2<float, double> { using type = double; };
  143. template <> struct promote_args_2<double, float> { using type = double; };
  144. template <> struct promote_args_2<float, long double> { using type = long double; };
  145. template <> struct promote_args_2<long double, float> { using type = long double; };
  146. template <> struct promote_args_2<double, long double> { using type = long double; };
  147. template <> struct promote_args_2<long double, double> { using type = long double; };
  148. #ifdef __STDCPP_FLOAT128_T__
  149. template <> struct promote_args_2<int, std::float128_t> { using type = std::float128_t; };
  150. template <> struct promote_args_2<std::float128_t, int> { using type = std::float128_t; };
  151. template <> struct promote_args_2<std::float128_t, float> { using type = std::float128_t; };
  152. template <> struct promote_args_2<float, std::float128_t> { using type = std::float128_t; };
  153. template <> struct promote_args_2<std::float128_t, double> { using type = std::float128_t; };
  154. template <> struct promote_args_2<double, std::float128_t> { using type = std::float128_t; };
  155. template <> struct promote_args_2<std::float128_t, long double> { using type = std::float128_t; };
  156. template <> struct promote_args_2<long double, std::float128_t> { using type = std::float128_t; };
  157. #ifdef __STDCPP_FLOAT16_T__
  158. template <> struct promote_args_2<std::float128_t, std::float16_t> { using type = std::float128_t; };
  159. template <> struct promote_args_2<std::float16_t, std::float128_t> { using type = std::float128_t; };
  160. #endif
  161. #ifdef __STDCPP_FLOAT32_T__
  162. template <> struct promote_args_2<std::float128_t, std::float32_t> { using type = std::float128_t; };
  163. template <> struct promote_args_2<std::float32_t, std::float128_t> { using type = std::float128_t; };
  164. #endif
  165. #ifdef __STDCPP_FLOAT64_T__
  166. template <> struct promote_args_2<std::float128_t, std::float64_t> { using type = std::float128_t; };
  167. template <> struct promote_args_2<std::float64_t, std::float128_t> { using type = std::float128_t; };
  168. #endif
  169. template <> struct promote_args_2<std::float128_t, std::float128_t> { using type = std::float128_t; };
  170. #endif
  171. #ifdef __STDCPP_FLOAT64_T__
  172. template <> struct promote_args_2<int, std::float64_t> { using type = std::float64_t; };
  173. template <> struct promote_args_2<std::float64_t, int> { using type = std::float64_t; };
  174. template <> struct promote_args_2<std::float64_t, float> { using type = std::float64_t; };
  175. template <> struct promote_args_2<float, std::float64_t> { using type = std::float64_t; };
  176. template <> struct promote_args_2<std::float64_t, double> { using type = std::float64_t; };
  177. template <> struct promote_args_2<double, std::float64_t> { using type = std::float64_t; };
  178. template <> struct promote_args_2<std::float64_t, long double> { using type = long double; };
  179. template <> struct promote_args_2<long double, std::float64_t> { using type = long double; };
  180. #ifdef __STDCPP_FLOAT16_T__
  181. template <> struct promote_args_2<std::float64_t, std::float16_t> { using type = std::float64_t; };
  182. template <> struct promote_args_2<std::float16_t, std::float64_t> { using type = std::float64_t; };
  183. #endif
  184. #ifdef __STDCPP_FLOAT32_T__
  185. template <> struct promote_args_2<std::float64_t, std::float32_t> { using type = std::float64_t; };
  186. template <> struct promote_args_2<std::float32_t, std::float64_t> { using type = std::float64_t; };
  187. #endif
  188. template <> struct promote_args_2<std::float64_t, std::float64_t> { using type = std::float64_t; };
  189. #endif
  190. #ifdef __STDCPP_FLOAT32_T__
  191. template <> struct promote_args_2<int, std::float32_t> { using type = std::float32_t; };
  192. template <> struct promote_args_2<std::float32_t, int> { using type = std::float32_t; };
  193. template <> struct promote_args_2<std::float32_t, float> { using type = std::float32_t; };
  194. template <> struct promote_args_2<float, std::float32_t> { using type = std::float32_t; };
  195. template <> struct promote_args_2<std::float32_t, double> { using type = double; };
  196. template <> struct promote_args_2<double, std::float32_t> { using type = double; };
  197. template <> struct promote_args_2<std::float32_t, long double> { using type = long double; };
  198. template <> struct promote_args_2<long double, std::float32_t> { using type = long double; };
  199. #ifdef __STDCPP_FLOAT16_T__
  200. template <> struct promote_args_2<std::float32_t, std::float16_t> { using type = std::float32_t; };
  201. template <> struct promote_args_2<std::float16_t, std::float32_t> { using type = std::float32_t; };
  202. #endif
  203. template <> struct promote_args_2<std::float32_t, std::float32_t> { using type = std::float32_t; };
  204. #endif
  205. #ifdef __STDCPP_FLOAT16_T__
  206. template <> struct promote_args_2<int, std::float16_t> { using type = std::float16_t; };
  207. template <> struct promote_args_2<std::float16_t, int> { using type = std::float16_t; };
  208. template <> struct promote_args_2<std::float16_t, float> { using type = float; };
  209. template <> struct promote_args_2<float, std::float16_t> { using type = float; };
  210. template <> struct promote_args_2<std::float16_t, double> { using type = double; };
  211. template <> struct promote_args_2<double, std::float16_t> { using type = double; };
  212. template <> struct promote_args_2<std::float16_t, long double> { using type = long double; };
  213. template <> struct promote_args_2<long double, std::float16_t> { using type = long double; };
  214. template <> struct promote_args_2<std::float16_t, std::float16_t> { using type = std::float16_t; };
  215. #endif
  216. template <typename T, typename U>
  217. using promote_args_2_t = typename promote_args_2<T, U>::type;
  218. template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
  219. struct promote_args
  220. {
  221. using type = typename promote_args_2<
  222. typename std::remove_cv<T1>::type,
  223. typename promote_args_2<
  224. typename std::remove_cv<T2>::type,
  225. typename promote_args_2<
  226. typename std::remove_cv<T3>::type,
  227. typename promote_args_2<
  228. typename std::remove_cv<T4>::type,
  229. typename promote_args_2<
  230. typename std::remove_cv<T5>::type, typename std::remove_cv<T6>::type
  231. >::type
  232. >::type
  233. >::type
  234. >::type
  235. >::type;
  236. #if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
  237. //
  238. // Guard against use of long double if it's not supported:
  239. //
  240. 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.");
  241. #endif
  242. };
  243. template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
  244. using promote_args_t = typename promote_args<T1, T2, T3, T4, T5, T6>::type;
  245. //
  246. // This struct is the same as above, but has no static assert on long double usage,
  247. // it should be used only on functions that can be implemented for long double
  248. // even when std lib support is missing or broken for that type.
  249. //
  250. template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
  251. struct promote_args_permissive
  252. {
  253. using type = typename promote_args_2<
  254. typename std::remove_cv<T1>::type,
  255. typename promote_args_2<
  256. typename std::remove_cv<T2>::type,
  257. typename promote_args_2<
  258. typename std::remove_cv<T3>::type,
  259. typename promote_args_2<
  260. typename std::remove_cv<T4>::type,
  261. typename promote_args_2<
  262. typename std::remove_cv<T5>::type, typename std::remove_cv<T6>::type
  263. >::type
  264. >::type
  265. >::type
  266. >::type
  267. >::type;
  268. };
  269. template <class T1, class T2=float, class T3=float, class T4=float, class T5=float, class T6=float>
  270. using promote_args_permissive_t = typename promote_args_permissive<T1, T2, T3, T4, T5, T6>::type;
  271. } // namespace tools
  272. } // namespace math
  273. } // namespace boost
  274. #endif // BOOST_MATH_PROMOTION_HPP