136 lines · plain
1// boost\math\tools\promotion.hpp2 3// Copyright John Maddock 2006.4// Copyright Paul A. Bristow 2006.5// Copyright Matt Borland 2023.6// Copyright Ryan Elandt 2023.7 8// Use, modification and distribution are subject to the9// Boost Software License, Version 1.0.10// (See accompanying file LICENSE_1_0.txt11// or copy at http://www.boost.org/LICENSE_1_0.txt)12 13// Promote arguments functions to allow math functions to have arguments14// provided as integer OR real (floating-point, built-in or UDT)15// (called ArithmeticType in functions that use promotion)16// that help to reduce the risk of creating multiple instantiations.17// Allows creation of an inline wrapper that forwards to a foo(RT, RT) function,18// so you never get to instantiate any mixed foo(RT, IT) functions.19 20#ifndef BOOST_MATH_PROMOTION_HPP21#define BOOST_MATH_PROMOTION_HPP22 23#ifdef _MSC_VER24#pragma once25#endif26 27#include <boost/math/tools/config.hpp>28#include <boost/math/tools/type_traits.hpp>29 30namespace boost31{32 namespace math33 {34 namespace tools35 {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 by40 // `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, as46 // identified by `std::is_floating_point`, then return the type47 // determined by `std::common_type`. Otherwise return the type using48 // an asymmetric convertibility rule.49 //50 ///// Discussion:51 //52 // If either T1 or T2 is an integer type,53 // pretend it was a double (for the purposes of further analysis).54 // Then pick the wider of the two floating-point types55 // as the actual signature to forward to.56 // For example: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 an64 // entirely float version is to call foo(1.F, 2.F). But since most (all?) the65 // math functions convert to double internally, probably there would not be the66 // hoped-for gain by using float here.67 //68 // This follows the C-compatible conversion rules of pow, etc69 // where pow(int, float) is converted to pow(double, double).70 71 72 // Promotes a single argument to double if it is an integer type73 template <class T>74 struct promote_arg {75 using type = typename boost::math::conditional<boost::math::is_integral<T>::value, double, T>::type;76 };77 78 79 // Promotes two arguments, neither of which is an integer type using an asymmetric80 // 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 88 template <class T1, class T2>89 struct pa2_integral_already_removed<T1, T2, true> {90 using type = boost::math::common_type_t<T1, T2, float>;91 };92 93 94 // Template definition for promote_args_permissive95 template <typename... Args>96 struct promote_args_permissive;97 // Specialization for one argument98 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 arguments103 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...>::type108 >::type;109 };110 111 template <class... Args>112 using promote_args_permissive_t = typename promote_args_permissive<Args...>::type;113 114 115 // Same as `promote_args_permissive` but with a static assertion that the promoted type116 // is not `long double` if `BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS` is defined117 template <class... Args>118 struct promote_args {119 using type = typename promote_args_permissive<Args...>::type;120#if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)121 //122 // Guard against use of long double if it's not supported:123 //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.");125#endif126 };127 128 template <class... Args>129 using promote_args_t = typename promote_args<Args...>::type;130 131 } // namespace tools132 } // namespace math133} // namespace boost134 135#endif // BOOST_MATH_PROMOTION_HPP136