Fix numext::round pre c++11 for large inputs.

This is to resolve an issue for large inputs when +0.5 can
actually lead to +1 if the input doesn't have enough precision
to resolve the addition - leading to an off-by-one error.

See discussion on 9a663973.
This commit is contained in:
Antonio Sanchez 2021-03-10 21:27:35 -08:00 committed by Rasmus Munk Larsen
parent c9d4367fa4
commit 14b7ebea11

View File

@ -476,18 +476,6 @@ inline NewType cast(const OldType& x)
* Implementation of round *
****************************************************************************/
#if EIGEN_HAS_CXX11_MATH
template<typename Scalar>
struct round_impl {
EIGEN_DEVICE_FUNC
static inline Scalar run(const Scalar& x)
{
EIGEN_STATIC_ASSERT((!NumTraits<Scalar>::IsComplex), NUMERIC_TYPE_MUST_BE_REAL)
EIGEN_USING_STD(round);
return Scalar(round(x));
}
};
#else
template<typename Scalar>
struct round_impl
{
@ -495,12 +483,28 @@ inline NewType cast(const OldType& x)
static inline Scalar run(const Scalar& x)
{
EIGEN_STATIC_ASSERT((!NumTraits<Scalar>::IsComplex), NUMERIC_TYPE_MUST_BE_REAL)
#if EIGEN_HAS_CXX11_MATH
EIGEN_USING_STD(round);
return Scalar(round(x));
#elif EIGEN_HAS_C99_MATH
if (is_same<Scalar, float>::value) {
return Scalar(::roundf(x));
} else {
return Scalar(round(x));
}
#else
EIGEN_USING_STD(floor);
EIGEN_USING_STD(ceil);
return (x > Scalar(0)) ? floor(x + Scalar(0.5)) : ceil(x - Scalar(0.5));
// If not enough precision to resolve a decimal at all, return the input.
// Otherwise, adding 0.5 can trigger an increment by 1.
const Scalar limit = Scalar(1ull << (NumTraits<Scalar>::digits() - 1));
if (x >= limit || x <= -limit) {
return x;
}
return (x > Scalar(0)) ? Scalar(floor(x + Scalar(0.5))) : Scalar(ceil(x - Scalar(0.5)));
#endif
}
};
#endif
template<typename Scalar>
struct round_retval