Skip to content

Commit

Permalink
Simplify and constexperize
Browse files Browse the repository at this point in the history
  • Loading branch information
mborland committed Mar 20, 2023
1 parent 5284fb7 commit 5a50ec7
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 50 deletions.
60 changes: 17 additions & 43 deletions include/boost/math/special_functions/round.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,17 @@
#include <limits>
#include <cmath>

#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L
#include <boost/math/ccmath/ldexp.hpp>
# ifndef BOOST_MATH_NO_CONSTEXPR_DETECTION
# define BOOST_MATH_HAS_CONSTEXPR_LDEXP
# endif
#endif

namespace boost{ namespace math{

namespace detail{

// https://stackoverflow.com/questions/8905246/how-to-check-if-float-can-be-exactly-represented-as-an-integer/17822304#17822304
template <typename T, typename ResultType>
inline ResultType float_to_int(T x)
{
BOOST_MATH_STD_USING

constexpr int sign = std::is_signed<ResultType>::value ? -1 : 0;

T y = floor(x);
if (y < static_cast<T>(0.0L))
{
return 0;
}

if (y >= ldexp(static_cast<T>(1.0L), (sizeof(ResultType) * CHAR_BIT) + sign) + sign)
{
return (std::numeric_limits<ResultType>::max)();
}

return static_cast<ResultType>(y);
}

template <typename T, typename TargetType>
inline bool is_representable(T x)
{
BOOST_MATH_STD_USING
return (floor(x) == x && x >= static_cast<T>(0.0L) && x < ldexp(static_cast<T>(1.0L), sizeof(TargetType) * CHAR_BIT));
}

template <class T, class Policy>
inline tools::promote_args_t<T> round(const T& v, const Policy& pol, const std::false_type&)
{
Expand Down Expand Up @@ -158,25 +136,21 @@ template <class T, class Policy>
inline long long llround(const T& v, const Policy& pol)
{
BOOST_MATH_STD_USING
using result_type = tools::promote_args_t<T>;
using result_type = boost::math::tools::promote_args_t<T>;

T r = boost::math::round(v, pol);
long long return_val = boost::math::detail::float_to_int<result_type, long long>(r);
bool representable = boost::math::detail::is_representable<result_type, long long>(r);
result_type r = boost::math::round(v, pol);

if ((return_val == (std::numeric_limits<long long>::max)() && !representable) ||
r < static_cast<result_type>((std::numeric_limits<long long>::min)()) ||
r > static_cast<result_type>((std::numeric_limits<long long>::max)()))
{
return static_cast<long long>(policies::raise_rounding_error("boost::math::llround<%1%>(%1%)", nullptr, v, static_cast<long long>(0), pol));
}
#ifdef BOOST_MATH_HAS_CONSTEXPR_LDEXP
constexpr result_type max_val = boost::math::ccmath::ldexp(static_cast<result_type>(1), std::numeric_limits<long long>::digits);
#else
static const result_type max_val = std::ldexp(static_cast<result_type>(1), std::numeric_limits<long long>::digits);
#endif

if (r < 0)
if (r >= max_val || r < -max_val)
{
return_val = static_cast<long long>(r);
return static_cast<long long>(boost::math::policies::raise_rounding_error("boost::math::llround<%1%>(%1%)", nullptr, v, static_cast<long long>(0), pol));
}

return return_val;
return static_cast<long long>(r);
}
template <class T>
inline long long llround(const T& v)
Expand Down
41 changes: 34 additions & 7 deletions test/git_issue_430.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,46 @@
// Copyright Matt Borland, 2023
// Copyright Matt Borland 2023
// Copyright John Maddock 2023
// 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)
//
// See: https://godbolt.org/z/Ev4ManrsW

#include <boost/math/special_functions/round.hpp>
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <cstdint>
#include "math_unit_test.hpp"

double x = 9223372036854775807.0; // can't be represented as double, will have a different value at runtime.
int main()
template <typename Real>
void test_llround_near_boundary()
{
int64_t result = boost::math::llround(x);
CHECK_EQUAL(result, INT64_C(9223372036854775807));
using std::ldexp;
Real boundary = ldexp(static_cast<Real>(1), std::numeric_limits<long long>::digits);

return boost::math::test::report_errors();
Real value;
int i;

for (value = boundary, i = 0; i < 100; value = boost::math::float_next(value), ++i)
{
BOOST_CHECK_THROW(boost::math::llround(value), boost::math::rounding_error);
}
for (value = boost::math::float_prior(boundary), i = 0; i < 1000; value = boost::math::float_prior(value), ++i)
{
BOOST_CHECK_EQUAL(static_cast<Real>(boost::math::llround(value)), boost::math::round(value));
}
for (value = boost::math::float_prior(-boundary), i = 0; i < 100; value = boost::math::float_prior(value), ++i)
{
BOOST_CHECK_THROW(boost::math::llround(value), boost::math::rounding_error);
}
for (value = -boundary, i = 0; i < 1000; value = boost::math::float_next(value), ++i)
{
BOOST_CHECK_EQUAL(static_cast<Real>(boost::math::llround(value)), boost::math::round(value));
}
}

BOOST_AUTO_TEST_CASE( test_main )
{
test_llround_near_boundary<float>();
test_llround_near_boundary<double>();
test_llround_near_boundary<long double>();
}

0 comments on commit 5a50ec7

Please sign in to comment.