2
0
mirror of https://github.com/boostorg/math.git synced 2026-02-28 05:22:33 +00:00

Fix llround for non-representable numbers

This commit is contained in:
Matt Borland
2023-03-16 19:26:23 -07:00
parent 53766896dd
commit 73b8ffc13c
3 changed files with 59 additions and 3 deletions

View File

@@ -14,11 +14,40 @@
#include <boost/math/policies/error_handling.hpp>
#include <boost/math/special_functions/math_fwd.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <limits>
#include <cmath>
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
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))
{
return (std::numeric_limits<ResultType>::max)();
}
return 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(1.0, 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&)
{
@@ -129,12 +158,21 @@ inline long long llround(const T& v, const Policy& pol)
using result_type = tools::promote_args_t<T>;
T r = boost::math::round(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)()))
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);
if (r < static_cast<result_type>((std::numeric_limits<long long>::min)()) ||
(return_val == LLONG_MAX && !representable))
{
return static_cast<long long>(policies::raise_rounding_error("boost::math::llround<%1%>(%1%)", nullptr, v, static_cast<long long>(0), pol));
}
return static_cast<long long>(r);
if (r < 0)
{
return_val = static_cast<long long>(r);
}
return return_val;
}
template <class T>
inline long long llround(const T& v)