promotion.hpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // boost\math\tools\promotion.hpp
  2. // Copyright John Maddock 2006.
  3. // Copyright Paul A. Bristow 2006.
  4. // Copyright Matt Borland 2023.
  5. // Copyright Ryan Elandt 2023.
  6. // Use, modification and distribution are subject to the
  7. // Boost Software License, Version 1.0.
  8. // (See accompanying file LICENSE_1_0.txt
  9. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. // Promote arguments functions to allow math functions to have arguments
  11. // provided as integer OR real (floating-point, built-in or UDT)
  12. // (called ArithmeticType in functions that use promotion)
  13. // that help to reduce the risk of creating multiple instantiations.
  14. // Allows creation of an inline wrapper that forwards to a foo(RT, RT) function,
  15. // so you never get to instantiate any mixed foo(RT, IT) functions.
  16. #ifndef BOOST_MATH_PROMOTION_HPP
  17. #define BOOST_MATH_PROMOTION_HPP
  18. #ifdef _MSC_VER
  19. #pragma once
  20. #endif
  21. #include <boost/math/tools/config.hpp>
  22. #include <boost/math/tools/type_traits.hpp>
  23. namespace boost
  24. {
  25. namespace math
  26. {
  27. namespace tools
  28. {
  29. ///// This promotion system works as follows:
  30. //
  31. // Rule<T1> (one argument promotion rule):
  32. // - Promotes `T` to `double` if `T` is an integer type as identified by
  33. // `std::is_integral`, otherwise is `T`
  34. //
  35. // Rule<T1, T2_to_TN...> (two or more argument promotion rule):
  36. // - 1. Calculates type using applying Rule<T1>.
  37. // - 2. Calculates type using applying Rule<T2_to_TN...>
  38. // - If the type calculated in 1 and 2 are both floating point types, as
  39. // identified by `std::is_floating_point`, then return the type
  40. // determined by `std::common_type`. Otherwise return the type using
  41. // an asymmetric convertibility rule.
  42. //
  43. ///// Discussion:
  44. //
  45. // If either T1 or T2 is an integer type,
  46. // pretend it was a double (for the purposes of further analysis).
  47. // Then pick the wider of the two floating-point types
  48. // as the actual signature to forward to.
  49. // For example:
  50. // foo(int, short) -> double foo(double, double); // ***NOT*** float foo(float, float)
  51. // foo(int, float) -> double foo(double, double); // ***NOT*** float foo(float, float)
  52. // foo(int, double) -> foo(double, double);
  53. // foo(double, float) -> double foo(double, double);
  54. // foo(double, float) -> double foo(double, double);
  55. // foo(any-int-or-float-type, long double) -> foo(long double, long double);
  56. // ONLY float foo(float, float) is unchanged, so the only way to get an
  57. // entirely float version is to call foo(1.F, 2.F). But since most (all?) the
  58. // math functions convert to double internally, probably there would not be the
  59. // hoped-for gain by using float here.
  60. //
  61. // This follows the C-compatible conversion rules of pow, etc
  62. // where pow(int, float) is converted to pow(double, double).
  63. // Promotes a single argument to double if it is an integer type
  64. template <class T>
  65. struct promote_arg {
  66. using type = typename boost::math::conditional<boost::math::is_integral<T>::value, double, T>::type;
  67. };
  68. // Promotes two arguments, neither of which is an integer type using an asymmetric
  69. // convertibility rule.
  70. template <class T1, class T2, bool = (boost::math::is_floating_point<T1>::value && boost::math::is_floating_point<T2>::value)>
  71. struct pa2_integral_already_removed {
  72. using type = typename boost::math::conditional<
  73. !boost::math::is_floating_point<T2>::value && boost::math::is_convertible<T1, T2>::value,
  74. T2, T1>::type;
  75. };
  76. // For two floating point types, promotes using `std::common_type` functionality
  77. template <class T1, class T2>
  78. struct pa2_integral_already_removed<T1, T2, true> {
  79. using type = boost::math::common_type_t<T1, T2, float>;
  80. };
  81. // Template definition for promote_args_permissive
  82. template <typename... Args>
  83. struct promote_args_permissive;
  84. // Specialization for one argument
  85. template <typename T>
  86. struct promote_args_permissive<T> {
  87. using type = typename promote_arg<typename boost::math::remove_cv<T>::type>::type;
  88. };
  89. // Specialization for two or more arguments
  90. template <typename T1, typename... T2_to_TN>
  91. struct promote_args_permissive<T1, T2_to_TN...> {
  92. using type = typename pa2_integral_already_removed<
  93. typename promote_args_permissive<T1>::type,
  94. typename promote_args_permissive<T2_to_TN...>::type
  95. >::type;
  96. };
  97. template <class... Args>
  98. using promote_args_permissive_t = typename promote_args_permissive<Args...>::type;
  99. // Same as `promote_args_permissive` but with a static assertion that the promoted type
  100. // is not `long double` if `BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS` is defined
  101. template <class... Args>
  102. struct promote_args {
  103. using type = typename promote_args_permissive<Args...>::type;
  104. #if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
  105. //
  106. // Guard against use of long double if it's not supported:
  107. //
  108. 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.");
  109. #endif
  110. };
  111. template <class... Args>
  112. using promote_args_t = typename promote_args<Args...>::type;
  113. } // namespace tools
  114. } // namespace math
  115. } // namespace boost
  116. #endif // BOOST_MATH_PROMOTION_HPP