Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constexpr frexp #686

Merged
merged 6 commits into from
Sep 6, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/sf/ccmath.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ All of the following functions require C++17 or greater.
template <typename T>
inline constexpr int fpclassify(T x);

template <typename Real>
inline constexpr Real frexp(Real arg, int* exp);

template <typename Z>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivial quibble, but I when I first read this I didn't grok that this was limited to integer arguments, maybe use <typename Integer> and perhaps a comment that this is for integer arguments?

Other than that, I'm happy for this to be merged, and you'll be pleased to know that ldexp should be noticeably easier! :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzmaddock : That was my (perhaps poor) convention; mimicking ℤ.

Is it possible to do a concept without breaking C++17 compatibility?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, although the enable_if does much the same thing. It's debatable whether the enable_if condition should be part of the declared interface - it clutters up the docs, but does at least make it clear what the requirements on Z are.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc suggestions have been incorporated in the latest commit.

As for concepts, you can avoid breaking C++17 compatibility using this macro (taken from good concepts):

#ifdef GOOD_COMPILER 
#define REQUIRES requires 
#elseif 
#define REQUIRES // 
#endif 

inline constexpr double frexp(Z arg, int* exp);

} // Namespaces

[endsect] [/section:ccmath Constexpr CMath]
1 change: 1 addition & 0 deletions include/boost/math/ccmath/ccmath.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
#include <boost/math/ccmath/isfinite.hpp>
#include <boost/math/ccmath/isnormal.hpp>
#include <boost/math/ccmath/fpclassify.hpp>
#include <boost/math/ccmath/frexp.hpp>

#endif // BOOST_MATH_CCMATH
98 changes: 98 additions & 0 deletions include/boost/math/ccmath/frexp.hpp
Original file line number Diff line number Diff line change
@@ -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 <cmath>
#include <limits>
#include <type_traits>
#include <boost/math/ccmath/isinf.hpp>
#include <boost/math/ccmath/isnan.hpp>
#include <boost/math/ccmath/isfinite.hpp>

namespace boost::math::ccmath {

namespace detail
{

template <typename Real>
inline constexpr Real frexp_zero_impl(Real arg, int* exp)
{
*exp = 0;
return arg;
}

template <typename Real>
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 <typename Real, std::enable_if_t<!std::is_integral_v<Real>, 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 <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
inline constexpr double frexp(Z arg, int* exp)
{
return boost::math::ccmath::frexp(static_cast<double>(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
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
Expand Down
80 changes: 80 additions & 0 deletions test/ccmath_frexp_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <cmath>
#include <cfloat>
#include <cstdint>
#include <limits>
#include <type_traits>
#include <boost/math/ccmath/frexp.hpp>
#include <boost/math/ccmath/isnan.hpp>
#include <boost/math/ccmath/isinf.hpp>

#ifdef BOOST_HAS_FLOAT128
#include <boost/multiprecision/float128.hpp>
#endif

template <typename T>
inline constexpr T base_helper(const T val)
{
int i = 0;
const T ans = boost::math::ccmath::frexp(val, &i);

return ans;
}

template <typename T>
inline constexpr int exp_helper(const T val)
{
int i = 0;
boost::math::ccmath::frexp(val, &i);

return i;
}

template <typename T>
constexpr void test()
{
if constexpr (std::numeric_limits<T>::has_quiet_NaN)
{
static_assert(boost::math::ccmath::isnan(base_helper(std::numeric_limits<T>::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<T>::infinity())), "If the arg is +- inf the value is returned");
static_assert(boost::math::ccmath::isinf(base_helper(-std::numeric_limits<T>::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<float>();
test<double>();

#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
test<long double>();
#endif

#ifdef BOOST_HAS_FLOAT128
test<boost::multiprecision::float128>();
#endif

return 0;
}
#else
int main()
{
return 0;
}
#endif
17 changes: 17 additions & 0 deletions test/compile_test/ccmath_frexp_incl_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/math/ccmath/frexp.hpp>
#include "test_compile_result.hpp"

void compile_and_link_test()
{
int i;
check_result<float>(boost::math::ccmath::frexp(1.0f, &i));
check_result<double>(boost::math::ccmath::frexp(1.0, &i));
#ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
check_result<long double>(boost::math::ccmath::frexp(1.0l, &i));
#endif
}