diff --git a/include/boost/math/ccmath/copysign.hpp b/include/boost/math/ccmath/copysign.hpp index 90a58102b1..e117e57faa 100644 --- a/include/boost/math/ccmath/copysign.hpp +++ b/include/boost/math/ccmath/copysign.hpp @@ -54,7 +54,7 @@ constexpr auto copysign(T1 mag, T2 sgn) noexcept { if (BOOST_MATH_IS_CONSTANT_EVALUATED(mag)) { - using promoted_type = boost::math::tools::promote_args_2_t; + using promoted_type = boost::math::tools::promote_args_t; return boost::math::ccmath::copysign(static_cast(mag), static_cast(sgn)); } else diff --git a/include/boost/math/ccmath/fdim.hpp b/include/boost/math/ccmath/fdim.hpp index 024eb9c934..3aeddda9b5 100644 --- a/include/boost/math/ccmath/fdim.hpp +++ b/include/boost/math/ccmath/fdim.hpp @@ -64,7 +64,7 @@ constexpr auto fdim(T1 x, T2 y) noexcept { if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) { - using promoted_type = boost::math::tools::promote_args_2_t; + using promoted_type = boost::math::tools::promote_args_t; return boost::math::ccmath::fdim(promoted_type(x), promoted_type(y)); } else diff --git a/include/boost/math/ccmath/fmax.hpp b/include/boost/math/ccmath/fmax.hpp index 0eb1ecbaae..e477b41dc6 100644 --- a/include/boost/math/ccmath/fmax.hpp +++ b/include/boost/math/ccmath/fmax.hpp @@ -60,7 +60,7 @@ constexpr auto fmax(T1 x, T2 y) noexcept { if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) { - using promoted_type = boost::math::tools::promote_args_2_t; + using promoted_type = boost::math::tools::promote_args_t; return boost::math::ccmath::fmax(static_cast(x), static_cast(y)); } else diff --git a/include/boost/math/ccmath/fmin.hpp b/include/boost/math/ccmath/fmin.hpp index 3de0b7e434..d2a8570755 100644 --- a/include/boost/math/ccmath/fmin.hpp +++ b/include/boost/math/ccmath/fmin.hpp @@ -60,7 +60,7 @@ constexpr auto fmin(T1 x, T2 y) noexcept { if (BOOST_MATH_IS_CONSTANT_EVALUATED(x)) { - using promoted_type = boost::math::tools::promote_args_2_t; + using promoted_type = boost::math::tools::promote_args_t; return boost::math::ccmath::fmin(static_cast(x), static_cast(y)); } else diff --git a/include/boost/math/ccmath/hypot.hpp b/include/boost/math/ccmath/hypot.hpp index 613b2681ac..9f866dbb70 100644 --- a/include/boost/math/ccmath/hypot.hpp +++ b/include/boost/math/ccmath/hypot.hpp @@ -87,7 +87,7 @@ constexpr auto hypot(T1 x, T2 y) noexcept { if(BOOST_MATH_IS_CONSTANT_EVALUATED(x)) { - using promoted_type = boost::math::tools::promote_args_2_t; + using promoted_type = boost::math::tools::promote_args_t; return boost::math::ccmath::hypot(static_cast(x), static_cast(y)); } else diff --git a/include/boost/math/differentiation/autodiff.hpp b/include/boost/math/differentiation/autodiff.hpp index 7cda196852..9ee5bdcd63 100644 --- a/include/boost/math/differentiation/autodiff.hpp +++ b/include/boost/math/differentiation/autodiff.hpp @@ -39,7 +39,7 @@ namespace detail { template struct promote_args_n { - using type = typename tools::promote_args_2::type>::type; + using type = typename tools::promote_args::type>::type; }; template @@ -2002,9 +2002,9 @@ using autodiff_root_type = typename autodiff_fvar_type::root_ty // See boost/math/tools/promotion.hpp template -struct promote_args_2, +struct promote_args, detail::autodiff_fvar_type> { - using type = detail::autodiff_fvar_type::type, + using type = detail::autodiff_fvar_type::type, #ifndef BOOST_NO_CXX14_CONSTEXPR (std::max)(Order0, Order1)>; #else @@ -2018,13 +2018,13 @@ struct promote_args> { }; template -struct promote_args_2, RealType1> { - using type = detail::autodiff_fvar_type::type, Order0>; +struct promote_args, RealType1> { + using type = detail::autodiff_fvar_type::type, Order0>; }; template -struct promote_args_2> { - using type = detail::autodiff_fvar_type::type, Order1>; +struct promote_args> { + using type = detail::autodiff_fvar_type::type, Order1>; }; template diff --git a/include/boost/math/policies/policy.hpp b/include/boost/math/policies/policy.hpp index 6daebaa64f..8d6b020f54 100644 --- a/include/boost/math/policies/policy.hpp +++ b/include/boost/math/policies/policy.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -723,22 +724,35 @@ inline constexpr typename normalise, A1, A2, A3, A4, A5, A6, A7, A8, A9 // // Traits class to handle internal promotion: // + template -struct evaluation -{ - typedef Real type; -}; +struct evaluation { + // Operation specific minimum float type + struct min_policy_allowed_float_type { + using type = float; + }; -template -struct evaluation -{ - using type = typename std::conditional::type; -}; + // Minimum type returned after applying double promotion rules + struct min_promote_with_float_type { + using type = typename std::conditional::type; + }; -template -struct evaluation -{ - using type = typename std::conditional::type; + // Minimum type returned after applying long double promotion rules + struct min_promote_with_double_type { + using type = typename std::conditional::type; + }; + + // Minimum type returned after applying double and long double promotion rules + struct min_evaluation_float_type { + using type = typename tools::promote_args::type; + }; + + using type = typename tools::promote_args::type; }; template diff --git a/include/boost/math/tools/promotion.hpp b/include/boost/math/tools/promotion.hpp index 842022694e..a09dcb23e4 100644 --- a/include/boost/math/tools/promotion.hpp +++ b/include/boost/math/tools/promotion.hpp @@ -3,6 +3,7 @@ // Copyright John Maddock 2006. // Copyright Paul A. Bristow 2006. // Copyright Matt Borland 2023. +// Copyright Ryan Elandt 2023. // Use, modification and distribution are subject to the // Boost Software License, Version 1.0. @@ -26,244 +27,96 @@ #include #include -#if defined __has_include -# if __cplusplus > 202002L || _MSVC_LANG > 202002L -# if __has_include () -# include -# endif -# endif -#endif - namespace boost { namespace math { namespace tools { + ///// This promotion system works as follows: + // + // Rule (one argument promotion rule): + // - Promotes `T` to `double` if `T` is an integer type as identified by + // `std::is_integral`, otherwise is `T` + // + // Rule (two or more argument promotion rule): + // - 1. Calculates type using applying Rule. + // - 2. Calculates type using applying Rule + // - If the type calculated in 1 and 2 are both floating point types, as + // identified by `std::is_floating_point`, then return the type + // determined by `std::common_type`. Otherwise return the type using + // an asymmetric convertibility rule. + // + ///// Discussion: + // // If either T1 or T2 is an integer type, // pretend it was a double (for the purposes of further analysis). // Then pick the wider of the two floating-point types // as the actual signature to forward to. // For example: - // foo(int, short) -> double foo(double, double); - // foo(int, float) -> double foo(double, double); - // Note: NOT float foo(float, float) - // foo(int, double) -> foo(double, double); - // foo(double, float) -> double foo(double, double); - // foo(double, float) -> double foo(double, double); - // foo(any-int-or-float-type, long double) -> foo(long double, long double); - // but ONLY float foo(float, float) is unchanged. - // So the only way to get an entirely float version is to call foo(1.F, 2.F), - // But since most (all?) the math functions convert to double internally, - // probably there would not be the hoped-for gain by using float here. - + // foo(int, short) -> double foo(double, double); // ***NOT*** float foo(float, float) + // foo(int, float) -> double foo(double, double); // ***NOT*** float foo(float, float) + // foo(int, double) -> foo(double, double); + // foo(double, float) -> double foo(double, double); + // foo(double, float) -> double foo(double, double); + // foo(any-int-or-float-type, long double) -> foo(long double, long double); + // ONLY float foo(float, float) is unchanged, so the only way to get an + // entirely float version is to call foo(1.F, 2.F). But since most (all?) the + // math functions convert to double internally, probably there would not be the + // hoped-for gain by using float here. + // // This follows the C-compatible conversion rules of pow, etc // where pow(int, float) is converted to pow(double, double). + + // Promotes a single argument to double if it is an integer type template - struct promote_arg - { // If T is integral type, then promote to double. - using type = typename std::conditional::value, double, T>::type; + struct promote_arg { + using type = typename std::conditional::value, double, T>::type; }; - // These full specialisations reduce std::conditional usage and speed up - // compilation: - template <> struct promote_arg { using type = float; }; - template <> struct promote_arg{ using type = double; }; - template <> struct promote_arg { using type = long double; }; - template <> struct promote_arg { using type = double; }; - #ifdef __STDCPP_FLOAT16_T__ - template <> struct promote_arg { using type = std::float16_t; }; - #endif - #ifdef __STDCPP_FLOAT32_T__ - template <> struct promote_arg { using type = std::float32_t; }; - #endif - #ifdef __STDCPP_FLOAT64_T__ - template <> struct promote_arg { using type = std::float64_t; }; - #endif - #ifdef __STDCPP_FLOAT128_T__ - template <> struct promote_arg { using type = std::float128_t; }; - #endif - - template - using promote_arg_t = typename promote_arg::type; + // Promotes two arguments, neither of which is an integer type using an asymmetric + // convertibility rule. + template ::value && std::is_floating_point::value)> + struct pa2_integral_already_removed { + using type = typename std::conditional< + !std::is_floating_point::value && std::is_convertible::value, + T2, T1>::type; + }; + // For two floating point types, promotes using `std::common_type` functionality template - struct promote_args_2 - { // Promote, if necessary, & pick the wider of the two floating-point types. - // for both parameter types, if integral promote to double. - using T1P = typename promote_arg::type; // T1 perhaps promoted. - using T2P = typename promote_arg::type; // T2 perhaps promoted. - using intermediate_type = typename std::conditional< - std::is_floating_point::value && std::is_floating_point::value, // both T1P and T2P are floating-point? -#ifdef __STDCPP_FLOAT128_T__ - typename std::conditional::value || std::is_same::value, // either long double? - std::float128_t, -#endif -#ifdef BOOST_MATH_USE_FLOAT128 - typename std::conditional::value || std::is_same<__float128, T2P>::value, // either long double? - __float128, -#endif - typename std::conditional::value || std::is_same::value, // either long double? - long double, // then result type is long double. -#ifdef __STDCPP_FLOAT64_T__ - typename std::conditional::value || std::is_same::value, // either float64? - std::float64_t, // then result type is float64_t. -#endif - typename std::conditional::value || std::is_same::value, // either double? - double, // result type is double. -#ifdef __STDCPP_FLOAT32_T__ - typename std::conditional::value || std::is_same::value, // either float32? - std::float32_t, // then result type is float32_t. -#endif - float // else result type is float. - >::type -#ifdef BOOST_MATH_USE_FLOAT128 - >::type -#endif -#ifdef __STDCPP_FLOAT128_T__ - >::type -#endif -#ifdef __STDCPP_FLOAT64_T__ - >::type -#endif -#ifdef __STDCPP_FLOAT32_T__ - >::type -#endif - >::type, - // else one or the other is a user-defined type: - typename std::conditional::value && std::is_convertible::value, T2P, T1P>::type>::type; - -#ifdef __STDCPP_FLOAT64_T__ - // If long doubles are doubles then we should prefer to use std::float64_t when available - using type = std::conditional_t<(sizeof(double) == sizeof(long double) && std::is_same::value), std::float64_t, intermediate_type>; -#else - using type = intermediate_type; -#endif - }; // promote_arg2 - // These full specialisations reduce std::conditional usage and speed up - // compilation: - template <> struct promote_args_2 { using type = float; }; - template <> struct promote_args_2{ using type = double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - - #ifdef __STDCPP_FLOAT128_T__ - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - - #ifdef __STDCPP_FLOAT16_T__ - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - #endif - - #ifdef __STDCPP_FLOAT32_T__ - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - #endif - - #ifdef __STDCPP_FLOAT64_T__ - template <> struct promote_args_2 { using type = std::float128_t; }; - template <> struct promote_args_2 { using type = std::float128_t; }; - #endif - - template <> struct promote_args_2 { using type = std::float128_t; }; - #endif - - #ifdef __STDCPP_FLOAT64_T__ - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - - #ifdef __STDCPP_FLOAT16_T__ - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - #endif - - #ifdef __STDCPP_FLOAT32_T__ - template <> struct promote_args_2 { using type = std::float64_t; }; - template <> struct promote_args_2 { using type = std::float64_t; }; - #endif - - template <> struct promote_args_2 { using type = std::float64_t; }; - #endif - - #ifdef __STDCPP_FLOAT32_T__ - template <> struct promote_args_2 { using type = std::float32_t; }; - template <> struct promote_args_2 { using type = std::float32_t; }; - template <> struct promote_args_2 { using type = std::float32_t; }; - template <> struct promote_args_2 { using type = std::float32_t; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - - #ifdef __STDCPP_FLOAT16_T__ - template <> struct promote_args_2 { using type = std::float32_t; }; - template <> struct promote_args_2 { using type = std::float32_t; }; - #endif - - template <> struct promote_args_2 { using type = std::float32_t; }; - #endif + struct pa2_integral_already_removed { + using type = std::common_type_t; + }; - #ifdef __STDCPP_FLOAT16_T__ - template <> struct promote_args_2 { using type = std::float16_t; }; - template <> struct promote_args_2 { using type = std::float16_t; }; - template <> struct promote_args_2 { using type = float; }; - template <> struct promote_args_2 { using type = float; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = long double; }; - template <> struct promote_args_2 { using type = std::float16_t; }; - #endif + // Template definition for promote_args_permissive + template + struct promote_args_permissive; + // Specialization for one argument + template + struct promote_args_permissive { + using type = typename promote_arg::type>::type; + }; + // Specialization for two or more arguments + template + struct promote_args_permissive { + using type = typename pa2_integral_already_removed< + typename promote_args_permissive::type, + typename promote_args_permissive::type + >::type; + }; - template - using promote_args_2_t = typename promote_args_2::type; + template + using promote_args_permissive_t = typename promote_args_permissive::type; - template - struct promote_args - { - using type = typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, typename std::remove_cv::type - >::type - >::type - >::type - >::type - >::type; + // Same as `promote_args_permissive` but with a static assertion that the promoted type + // is not `long double` if `BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS` is defined + template + struct promote_args { + using type = typename promote_args_permissive::type; #if defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) // // Guard against use of long double if it's not supported: @@ -272,40 +125,11 @@ namespace boost #endif }; - template - using promote_args_t = typename promote_args::type; - - // - // This struct is the same as above, but has no static assert on long double usage, - // it should be used only on functions that can be implemented for long double - // even when std lib support is missing or broken for that type. - // - template - struct promote_args_permissive - { - using type = typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, - typename promote_args_2< - typename std::remove_cv::type, typename std::remove_cv::type - >::type - >::type - >::type - >::type - >::type; - }; - - template - using promote_args_permissive_t = typename promote_args_permissive::type; + template + using promote_args_t = typename promote_args::type; } // namespace tools } // namespace math } // namespace boost #endif // BOOST_MATH_PROMOTION_HPP - diff --git a/test/compile_test/test_promote_args.cpp b/test/compile_test/test_promote_args.cpp index 2281aa7115..3205f139a3 100644 --- a/test/compile_test/test_promote_args.cpp +++ b/test/compile_test/test_promote_args.cpp @@ -89,7 +89,7 @@ int main() static_assert(std::is_same_v, double>); static_assert(std::is_same_v, long double>); #ifdef __STDCPP_FLOAT16_T__ - static_assert(std::is_same_v, float>); + static_assert(std::is_same_v, std::float16_t>); #endif #ifdef __STDCPP_FLOAT32_T__ static_assert(std::is_same_v, std::float32_t>);