From a98da2a9f0705dbadcb51c7faa3e69b0d4f7af3d Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 27 Aug 2023 11:31:53 -0400 Subject: [PATCH 1/2] #1021 chunk 1 --- include/boost/math/ccmath/copysign.hpp | 2 +- include/boost/math/ccmath/fdim.hpp | 2 +- include/boost/math/ccmath/fmax.hpp | 2 +- include/boost/math/ccmath/fmin.hpp | 2 +- include/boost/math/ccmath/hypot.hpp | 2 +- .../boost/math/differentiation/autodiff.hpp | 14 +- include/boost/math/tools/promotion.hpp | 311 ++++-------------- 7 files changed, 81 insertions(+), 254 deletions(-) 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/tools/promotion.hpp b/include/boost/math/tools/promotion.hpp index 7b7789d037..e9df710013 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,11 +27,6 @@ #include #include -#if defined __has_include -# if __has_include () -# include -# endif -#endif namespace boost { @@ -38,230 +34,90 @@ namespace boost { 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: @@ -270,40 +126,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 - From 4c9592c365c26ee7dac09d4f8ecc6c6aecd3777d Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 28 Aug 2023 13:21:14 -0400 Subject: [PATCH 2/2] Merge branch 'develop' into promotion_2 --- include/boost/math/concepts/real_concept.hpp | 8 ++++++-- include/boost/math/tools/config.hpp | 10 +++++----- include/boost/math/tools/promotion.hpp | 1 - 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/boost/math/concepts/real_concept.hpp b/include/boost/math/concepts/real_concept.hpp index 56e4342e6b..565d58e421 100644 --- a/include/boost/math/concepts/real_concept.hpp +++ b/include/boost/math/concepts/real_concept.hpp @@ -45,8 +45,12 @@ # include #endif -#if __has_include() -# include +#if defined __has_include +# if __cplusplus > 202002L || _MSVC_LANG > 202002L +# if __has_include () +# include +# endif +# endif #endif namespace boost{ namespace math{ diff --git a/include/boost/math/tools/config.hpp b/include/boost/math/tools/config.hpp index 8316c0bc78..b1d93bc3d2 100644 --- a/include/boost/math/tools/config.hpp +++ b/include/boost/math/tools/config.hpp @@ -152,11 +152,11 @@ // libstdc++3 only defines to/from_chars for std::float128_t when one of these defines are set // otherwise we're right out of luck... # if defined(_GLIBCXX_LDOUBLE_IS_IEEE_BINARY128) || defined(_GLIBCXX_HAVE_FLOAT128_MATH) -# include // std::strlen is used with from_chars -# include -# include -# define BOOST_MATH_USE_CHARCONV_FOR_CONVERSION -#endif +# include // std::strlen is used with from_chars +# include +# include +# define BOOST_MATH_USE_CHARCONV_FOR_CONVERSION +# endif # endif #endif diff --git a/include/boost/math/tools/promotion.hpp b/include/boost/math/tools/promotion.hpp index e9df710013..f48ae515e8 100644 --- a/include/boost/math/tools/promotion.hpp +++ b/include/boost/math/tools/promotion.hpp @@ -27,7 +27,6 @@ #include #include - namespace boost { namespace math