2
0
mirror of https://github.com/boostorg/math.git synced 2026-01-19 04:22:09 +00:00

Improve trunc handling

This commit is contained in:
Matt Borland
2023-04-03 14:50:22 +02:00
parent dc676d54f9
commit e5b4f3f0c3
3 changed files with 125 additions and 4 deletions

View File

@@ -1,4 +1,5 @@
// Copyright John Maddock 2007. // Copyright John Maddock 2007.
// Copyright Matt Borland 2023.
// Use, modification and distribution are subject to the // Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file // Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

View File

@@ -1,4 +1,5 @@
// Copyright John Maddock 2007. // Copyright John Maddock 2007.
// Copyright Matt Borland 2023.
// Use, modification and distribution are subject to the // Use, modification and distribution are subject to the
// Boost Software License, Version 1.0. (See accompanying file // Boost Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -16,6 +17,13 @@
#include <boost/math/policies/error_handling.hpp> #include <boost/math/policies/error_handling.hpp>
#include <boost/math/special_functions/fpclassify.hpp> #include <boost/math/special_functions/fpclassify.hpp>
#if __cplusplus >= 201703L || _MSVC_LANG >= 201703L
#include <boost/math/ccmath/ldexp.hpp>
# if !defined(BOOST_MATH_NO_CONSTEXPR_DETECTION) && !defined(TEST_GROUP_7) // Concept tests throw off constexpr ldexp
# define BOOST_MATH_HAS_CONSTEXPR_LDEXP
# endif
#endif
namespace boost{ namespace math{ namespace detail{ namespace boost{ namespace math{ namespace detail{
template <class T, class Policy> template <class T, class Policy>
@@ -67,7 +75,14 @@ inline int itrunc(const T& v, const Policy& pol)
BOOST_MATH_STD_USING BOOST_MATH_STD_USING
using result_type = tools::promote_args_t<T>; using result_type = tools::promote_args_t<T>;
result_type r = boost::math::trunc(v, pol); result_type r = boost::math::trunc(v, pol);
if(r > static_cast<result_type>((std::numeric_limits<int>::max)()) || r < static_cast<result_type>((std::numeric_limits<int>::min)()))
#ifdef BOOST_MATH_HAS_CONSTEXPR_LDEXP
constexpr result_type max_val = boost::math::ccmath::ldexp(static_cast<result_type>(1), std::numeric_limits<int>::digits);
#else
static const result_type max_val = ldexp(static_cast<result_type>(1), std::numeric_limits<int>::digits);
#endif
if(r >= max_val || r < -max_val)
{ {
return static_cast<int>(policies::raise_rounding_error("boost::math::itrunc<%1%>(%1%)", nullptr, static_cast<result_type>(v), 0, pol)); return static_cast<int>(policies::raise_rounding_error("boost::math::itrunc<%1%>(%1%)", nullptr, static_cast<result_type>(v), 0, pol));
} }
@@ -85,7 +100,14 @@ inline long ltrunc(const T& v, const Policy& pol)
BOOST_MATH_STD_USING BOOST_MATH_STD_USING
using result_type = tools::promote_args_t<T>; using result_type = tools::promote_args_t<T>;
result_type r = boost::math::trunc(v, pol); result_type r = boost::math::trunc(v, pol);
if(r > static_cast<result_type>((std::numeric_limits<long>::max)()) || r < static_cast<result_type>((std::numeric_limits<long>::min)()))
#ifdef BOOST_MATH_HAS_CONSTEXPR_LDEXP
constexpr result_type max_val = boost::math::ccmath::ldexp(static_cast<result_type>(1), std::numeric_limits<long>::digits);
#else
static const result_type max_val = ldexp(static_cast<result_type>(1), std::numeric_limits<long>::digits);
#endif
if(r >= max_val || r < -max_val)
{ {
return static_cast<long>(policies::raise_rounding_error("boost::math::ltrunc<%1%>(%1%)", nullptr, static_cast<result_type>(v), 0L, pol)); return static_cast<long>(policies::raise_rounding_error("boost::math::ltrunc<%1%>(%1%)", nullptr, static_cast<result_type>(v), 0L, pol));
} }
@@ -103,8 +125,14 @@ inline long long lltrunc(const T& v, const Policy& pol)
BOOST_MATH_STD_USING BOOST_MATH_STD_USING
using result_type = tools::promote_args_t<T>; using result_type = tools::promote_args_t<T>;
result_type r = boost::math::trunc(v, pol); result_type r = boost::math::trunc(v, pol);
if(r > static_cast<result_type>((std::numeric_limits<long long>::max)()) ||
r < static_cast<result_type>((std::numeric_limits<long long>::min)())) #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 = ldexp(static_cast<result_type>(1), std::numeric_limits<long long>::digits);
#endif
if(r >= max_val || r < -max_val)
{ {
return static_cast<long long>(policies::raise_rounding_error("boost::math::lltrunc<%1%>(%1%)", nullptr, v, static_cast<long long>(0), pol)); return static_cast<long long>(policies::raise_rounding_error("boost::math::lltrunc<%1%>(%1%)", nullptr, v, static_cast<long long>(0), pol));
} }

View File

@@ -7,6 +7,7 @@
// See: https://godbolt.org/z/Ev4ManrsW // See: https://godbolt.org/z/Ev4ManrsW
#include <boost/math/special_functions/round.hpp> #include <boost/math/special_functions/round.hpp>
#include <boost/math/special_functions/trunc.hpp>
#include <boost/math/special_functions/next.hpp> #include <boost/math/special_functions/next.hpp>
#define BOOST_TEST_MAIN #define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
@@ -94,12 +95,103 @@ void test_iround_near_boundary()
} }
} }
template <typename Real>
void test_lltrunc_near_boundary()
{
using std::ldexp;
Real boundary = ldexp(static_cast<Real>(1), std::numeric_limits<long long>::digits);
Real value;
int i;
for (value = boundary, i = 0; i < 100; value = boost::math::float_next(value), ++i)
{
BOOST_CHECK_THROW(boost::math::lltrunc(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::lltrunc(value)), boost::math::lltrunc(value));
}
for (value = boost::math::float_prior(-boundary), i = 0; i < 100; value = boost::math::float_prior(value), ++i)
{
BOOST_CHECK_THROW(boost::math::lltrunc(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::lltrunc(value)), boost::math::lltrunc(value));
}
}
template <typename Real>
void test_ltrunc_near_boundary()
{
using std::ldexp;
Real boundary = ldexp(static_cast<Real>(1), std::numeric_limits<long>::digits);
Real value;
int i;
for (value = boundary, i = 0; i < 100; value = boost::math::float_next(value), ++i)
{
BOOST_CHECK_THROW(boost::math::ltrunc(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::ltrunc(value)), boost::math::ltrunc(value));
}
for (value = boost::math::float_prior(-boundary), i = 0; i < 100; value = boost::math::float_prior(value), ++i)
{
BOOST_CHECK_THROW(boost::math::ltrunc(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::ltrunc(value)), boost::math::ltrunc(value));
}
}
template <typename Real>
void test_itrunc_near_boundary()
{
using std::ldexp;
Real boundary = ldexp(static_cast<Real>(1), std::numeric_limits<int>::digits);
Real value;
int i;
for (value = boundary, i = 0; i < 100; value = boost::math::float_next(value), ++i)
{
BOOST_CHECK_THROW(boost::math::itrunc(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::itrunc(value)), boost::math::itrunc(value));
}
for (value = boost::math::float_prior(-boundary), i = 0; i < 100; value = boost::math::float_prior(value), ++i)
{
BOOST_CHECK_THROW(boost::math::itrunc(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::itrunc(value)), boost::math::itrunc(value));
}
}
BOOST_AUTO_TEST_CASE( test_main ) BOOST_AUTO_TEST_CASE( test_main )
{ {
// Round
test_llround_near_boundary<float>(); test_llround_near_boundary<float>();
test_llround_near_boundary<double>(); test_llround_near_boundary<double>();
test_lround_near_boundary<float>(); test_lround_near_boundary<float>();
test_iround_near_boundary<float>(); test_iround_near_boundary<float>();
// Trunc
test_lltrunc_near_boundary<float>();
test_lltrunc_near_boundary<double>();
test_ltrunc_near_boundary<float>();
test_itrunc_near_boundary<float>();
} }