diff --git a/doc/sf/ccmath.qbk b/doc/sf/ccmath.qbk index 62bcc1ac9d..5f4b2f4134 100644 --- a/doc/sf/ccmath.qbk +++ b/doc/sf/ccmath.qbk @@ -12,6 +12,7 @@ LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) `Constexpr` implementations of the functionality found in ``. In a `constexpr` context the functions will use an implementation defined in boost. If the context is not `constexpr` the functionality will be directly from the STL implementation of `` used by the compiler. +All functions that take an `Integer` type and return a `double` simply cast the `Integer` argument to a `double`. All of the following functions require C++17 or greater. [heading Synopsis] @@ -30,8 +31,8 @@ All of the following functions require C++17 or greater. template inline constexpr Real sqrt(Real x); - template - inline constexpr double sqrt(Z x); + template + inline constexpr double sqrt(Integer x); template inline constexpr T abs(T x); @@ -51,6 +52,12 @@ All of the following functions require C++17 or greater. template inline constexpr int fpclassify(T x); + template + inline constexpr Real frexp(Real arg, int* exp); + + template + inline constexpr double frexp(Integer arg, int* exp); + } // Namespaces [endsect] [/section:ccmath Constexpr CMath] diff --git a/include/boost/math/ccmath/ccmath.hpp b/include/boost/math/ccmath/ccmath.hpp index bdcd895620..37b613dfd6 100644 --- a/include/boost/math/ccmath/ccmath.hpp +++ b/include/boost/math/ccmath/ccmath.hpp @@ -15,5 +15,6 @@ #include #include #include +#include #endif // BOOST_MATH_CCMATH diff --git a/include/boost/math/ccmath/frexp.hpp b/include/boost/math/ccmath/frexp.hpp new file mode 100644 index 0000000000..88b520c1ec --- /dev/null +++ b/include/boost/math/ccmath/frexp.hpp @@ -0,0 +1,98 @@ +// (C) Copyright Christopher Kormanyos 1999 - 2021. +// (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_FREXP_HPP +#define BOOST_MATH_CCMATH_FREXP_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost::math::ccmath { + +namespace detail +{ + +template +inline constexpr Real frexp_zero_impl(Real arg, int* exp) +{ + *exp = 0; + return arg; +} + +template +inline constexpr Real frexp_impl(Real arg, int* exp) +{ + const bool negative_arg = (arg < Real(0)); + + Real f = negative_arg ? -arg : arg; + int e2 = 0; + constexpr Real two_pow_32 = Real(4294967296); + + while (f >= two_pow_32) + { + f = f / two_pow_32; + e2 += 32; + } + + while(f >= Real(1)) + { + f = f / Real(2); + ++e2; + } + + if(exp != nullptr) + { + *exp = e2; + } + + return !negative_arg ? f : -f; +} + +} // namespace detail + +template , bool> = true> +inline constexpr Real frexp(Real arg, int* exp) +{ + if(BOOST_MATH_IS_CONSTANT_EVALUATED(arg)) + { + return arg == Real(0) ? detail::frexp_zero_impl(arg, exp) : + arg == Real(-0) ? detail::frexp_zero_impl(arg, exp) : + boost::math::ccmath::isinf(arg) ? detail::frexp_zero_impl(arg, exp) : + boost::math::ccmath::isnan(arg) ? detail::frexp_zero_impl(arg, exp) : + boost::math::ccmath::detail::frexp_impl(arg, exp); + } + else + { + using std::frexp; + return frexp(arg, exp); + } +} + +template , bool> = true> +inline constexpr double frexp(Z arg, int* exp) +{ + return boost::math::ccmath::frexp(static_cast(arg), exp); +} + +inline constexpr float frexpf(float arg, int* exp) +{ + return boost::math::ccmath::frexp(arg, exp); +} + +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS +inline constexpr long double frexpl(long double arg, int* exp) +{ + return boost::math::ccmath::frexp(arg, exp); +} +#endif + +} + +#endif // BOOST_MATH_CCMATH_FREXP_HPP diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 52fb87f5e5..f867dd882d 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -128,6 +128,7 @@ test-suite special_fun : [ run ccmath_isfinite_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_isnormal_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] [ run ccmath_fpclassify_test.cpp ../../test/build//boost_unit_test_framework : : : [ requires cxx17_if_constexpr ] ] + [ run ccmath_frexp_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_frexp_test.cpp b/test/ccmath_frexp_test.cpp new file mode 100644 index 0000000000..75a11b26a5 --- /dev/null +++ b/test/ccmath_frexp_test.cpp @@ -0,0 +1,80 @@ +// (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 + +#ifdef BOOST_HAS_FLOAT128 +#include +#endif + +template +inline constexpr T base_helper(const T val) +{ + int i = 0; + const T ans = boost::math::ccmath::frexp(val, &i); + + return ans; +} + +template +inline constexpr int exp_helper(const T val) +{ + int i = 0; + boost::math::ccmath::frexp(val, &i); + + return i; +} + +template +constexpr void test() +{ + if constexpr (std::numeric_limits::has_quiet_NaN) + { + static_assert(boost::math::ccmath::isnan(base_helper(std::numeric_limits::quiet_NaN())), "If the arg is NaN, NaN is returned"); + } + + static_assert(!base_helper(T(0)), "If the arg is +- 0 the value is returned"); + static_assert(!base_helper(T(-0)), "If the arg is +- 0 the value is returned"); + static_assert(boost::math::ccmath::isinf(base_helper(std::numeric_limits::infinity())), "If the arg is +- inf the value is returned"); + static_assert(boost::math::ccmath::isinf(base_helper(-std::numeric_limits::infinity())), "If the arg is +- inf the value is returned"); + + // N[125/32, 30] + // 3.90625000000000000000000000000 + // 0.976562500000000000000000000000 * 2^2 + constexpr T test_base = base_helper(T(125.0/32)); + static_assert(test_base == T(0.9765625)); + constexpr int test_exp = exp_helper(T(125.0/32)); + static_assert(test_exp == 2); +} + +#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_frexp_incl_test.cpp b/test/compile_test/ccmath_frexp_incl_test.cpp new file mode 100644 index 0000000000..a273d919f2 --- /dev/null +++ b/test/compile_test/ccmath_frexp_incl_test.cpp @@ -0,0 +1,17 @@ +// (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() +{ + int i; + check_result(boost::math::ccmath::frexp(1.0f, &i)); + check_result(boost::math::ccmath::frexp(1.0, &i)); +#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS + check_result(boost::math::ccmath::frexp(1.0l, &i)); +#endif +}