From 8e55178e922cb21e17929a1ba1cff2977d65aafc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 23 Sep 2021 20:58:07 +0300 Subject: [PATCH] constexpr modf --- doc/sf/ccmath.qbk | 3 + include/boost/math/ccmath/ccmath.hpp | 1 + include/boost/math/ccmath/modf.hpp | 77 ++++++++++++++++ test/Jamfile.v2 | 1 + test/ccmath_modf_test.cpp | 98 +++++++++++++++++++++ test/compile_test/ccmath_modf_incl_test.cpp | 21 +++++ 6 files changed, 201 insertions(+) create mode 100644 include/boost/math/ccmath/modf.hpp create mode 100644 test/ccmath_modf_test.cpp create mode 100644 test/compile_test/ccmath_modf_incl_test.cpp diff --git a/doc/sf/ccmath.qbk b/doc/sf/ccmath.qbk index 37d1a4a8de..a8923979e7 100644 --- a/doc/sf/ccmath.qbk +++ b/doc/sf/ccmath.qbk @@ -109,6 +109,9 @@ All of the following functions require C++17 or greater. template inline constexpr double trunc(Integer arg) noexcept + template + inline constexpr Real modf(Real x, Real* iptr) noexcept + } // Namespaces [endsect] [/section:ccmath Constexpr CMath] diff --git a/include/boost/math/ccmath/ccmath.hpp b/include/boost/math/ccmath/ccmath.hpp index 5c3ce471dc..4f6d03ef05 100644 --- a/include/boost/math/ccmath/ccmath.hpp +++ b/include/boost/math/ccmath/ccmath.hpp @@ -24,5 +24,6 @@ #include #include #include +#include #endif // BOOST_MATH_CCMATH_HPP diff --git a/include/boost/math/ccmath/modf.hpp b/include/boost/math/ccmath/modf.hpp new file mode 100644 index 0000000000..479f6432e8 --- /dev/null +++ b/include/boost/math/ccmath/modf.hpp @@ -0,0 +1,77 @@ +// (C) Copyright Matt Borland 2021. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_MATH_CCMATH_MODF_HPP +#define BOOST_MATH_CCMATH_MODF_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost::math::ccmath { + +namespace detail { + +template +inline constexpr Real modf_error_impl(Real x, Real* iptr) +{ + *iptr = x; + return boost::math::ccmath::abs(x) == Real(0) ? x : + x > Real(0) ? Real(0) : -Real(0); +} + +template +inline constexpr Real modf_nan_impl(Real x, Real* iptr) +{ + *iptr = x; + return x; +} + +template +inline constexpr Real modf_impl(Real x, Real* iptr) +{ + *iptr = boost::math::ccmath::trunc(x); + return (x - *iptr); +} + +} // Namespace detail + +template +inline constexpr Real modf(Real x, Real* iptr) +{ + if(BOOST_MATH_IS_CONSTANT_EVALUATED(x)) + { + return boost::math::ccmath::abs(x) == Real(0) ? detail::modf_error_impl(x, iptr) : + boost::math::ccmath::isinf(x) ? detail::modf_error_impl(x, iptr) : + boost::math::ccmath::isnan(x) ? detail::modf_nan_impl(x, iptr) : + boost::math::ccmath::detail::modf_impl(x, iptr); + } + else + { + using std::modf; + return modf(x, iptr); + } +} + +inline constexpr float modff(float x, float* iptr) +{ + return boost::math::ccmath::modf(x, iptr); +} + +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS +inline constexpr long double modfl(long double x, long double* iptr) +{ + return boost::math::ccmath::modf(x, iptr); +} +#endif + +} // Namespaces + +#endif // BOOST_MATH_CCMATH_MODF_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e8744a6ba2..1ae431bd07 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -138,6 +138,7 @@ test-suite special_fun : [ run ccmath_floor_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_ceil_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_trunc_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] + [ run ccmath_modf_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run log1p_expm1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run powm1_sqrtp1m1_test.cpp test_instances//test_instances pch_light ../../test/build//boost_unit_test_framework ] [ run special_functions_test.cpp ../../test/build//boost_unit_test_framework ] diff --git a/test/ccmath_modf_test.cpp b/test/ccmath_modf_test.cpp new file mode 100644 index 0000000000..eadd22b5c7 --- /dev/null +++ b/test/ccmath_modf_test.cpp @@ -0,0 +1,98 @@ +// (C) Copyright Matt Borland 2021. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_HAS_FLOAT128 +#include +#endif + +template +inline constexpr T floating_point_value(const T val) +{ + T i = 0; + const T ans = boost::math::ccmath::modf(val, &i); + + return ans; +} + +template +inline constexpr T integral_value(const T val) +{ + T i = 0; + boost::math::ccmath::modf(val, &i); + + return i; +} + +template +inline constexpr std::pair pair_value(const T val) +{ + T i = 0; + T ans = boost::math::ccmath::modf(val, &i); + + return std::make_pair(i, ans); +} + +template +constexpr void test() +{ + if constexpr (std::numeric_limits::has_quiet_NaN) + { + constexpr std::pair NaN_val = pair_value(std::numeric_limits::quiet_NaN()); + static_assert(boost::math::ccmath::isnan(NaN_val.first)); + static_assert(boost::math::ccmath::isnan(NaN_val.second)); + } + + // if x is +-0, +-0 is returned and +-0 is stored in *iptr + static_assert(floating_point_value(T(0)) == 0); + static_assert(floating_point_value(-T(0)) == -0); + static_assert(integral_value(T(0)) == 0); + static_assert(integral_value(-T(0)) == -0); + + // if x is +- inf, +-0 is returned and +-inf is stored in *iptr + static_assert(floating_point_value(std::numeric_limits::infinity()) == 0); + static_assert(floating_point_value(-std::numeric_limits::infinity()) == -0); + static_assert(integral_value(std::numeric_limits::infinity()) == std::numeric_limits::infinity()); + static_assert(integral_value(-std::numeric_limits::infinity()) == -std::numeric_limits::infinity()); + + // The returned value is exact, the current rounding mode is ignored + // The return value and *iptr each have the same type and sign as x + static_assert(integral_value(T(123.45)) == 123); + static_assert(integral_value(T(-234.56)) == -234); + static_assert(floating_point_value(T(1.0/2)) == T(1.0/2)); + static_assert(floating_point_value(T(-1.0/3)) == T(-1.0/3)); +} + +#if !defined(BOOST_MATH_NO_CONSTEXPR_DETECTION) && !defined(BOOST_MATH_USING_BUILTIN_CONSTANT_P) +int main() +{ + test(); + test(); + + #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + test(); + #endif + + #ifdef BOOST_HAS_FLOAT128 + test(); + #endif + + return 0; +} +#else +int main() +{ + return 0; +} +#endif diff --git a/test/compile_test/ccmath_modf_incl_test.cpp b/test/compile_test/ccmath_modf_incl_test.cpp new file mode 100644 index 0000000000..ac23cb73bc --- /dev/null +++ b/test/compile_test/ccmath_modf_incl_test.cpp @@ -0,0 +1,21 @@ +// (C) Copyright Matt Borland 2021. +// Use, modification and distribution are subject to the +// Boost Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include "test_compile_result.hpp" + +void compile_and_link_test() +{ + float i_f; + check_result(boost::math::ccmath::modf(1.0f, &i_f)); + + double i_d; + check_result(boost::math::ccmath::modf(1.0, &i_d)); + +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + long double i_ld; + check_result(boost::math::ccmath::modf(1.0l, &i_ld)); +#endif +}