Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
43 changes: 23 additions & 20 deletions libc/src/__support/FPUtil/NearestIntegerOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -346,12 +346,13 @@ fromfpx(T x, int rnd, unsigned int width) {

namespace internal {

template <typename F, typename I,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<I>,
int> = 0>
LIBC_INLINE I rounded_float_to_signed_integer(F x) {
constexpr I INTEGER_MIN = (I(1) << (sizeof(I) * 8 - 1));
constexpr I INTEGER_MAX = -(INTEGER_MIN + 1);
template <
typename F, typename InType,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<InType>,
int> = 0>
LIBC_INLINE InType rounded_float_to_signed_integer(F x) {
constexpr InType INTEGER_MIN = (InType(1) << (sizeof(InType) * 8 - 1));
constexpr InType INTEGER_MAX = -(INTEGER_MIN + 1);
FPBits<F> bits(x);
auto set_domain_error_and_raise_invalid = []() {
set_errno_if_required(EDOM);
Expand All @@ -364,7 +365,7 @@ LIBC_INLINE I rounded_float_to_signed_integer(F x) {
}

int exponent = bits.get_exponent();
constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1;
constexpr int EXPONENT_LIMIT = sizeof(InType) * 8 - 1;
if (exponent > EXPONENT_LIMIT) {
set_domain_error_and_raise_invalid();
return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX;
Expand All @@ -374,29 +375,31 @@ LIBC_INLINE I rounded_float_to_signed_integer(F x) {
return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX;
}
// If the control reaches here, then it means that the rounded
// value is the most negative number for the signed integer type I.
// value is the most negative number for the signed integer type InType.
}

// For all other cases, if `x` can fit in the integer type `I`,
// For all other cases, if `x` can fit in the integer type `InType`,
// we just return `x`. static_cast will convert the floating
// point value to the exact integer value.
return static_cast<I>(x);
return static_cast<InType>(x);
}

} // namespace internal

template <typename F, typename I,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<I>,
int> = 0>
LIBC_INLINE I round_to_signed_integer(F x) {
return internal::rounded_float_to_signed_integer<F, I>(round(x));
template <
typename F, typename InType,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<InType>,
int> = 0>
LIBC_INLINE InType round_to_signed_integer(F x) {
return internal::rounded_float_to_signed_integer<F, InType>(round(x));
}

template <typename F, typename I,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<I>,
int> = 0>
LIBC_INLINE I round_to_signed_integer_using_current_rounding_mode(F x) {
return internal::rounded_float_to_signed_integer<F, I>(
template <
typename F, typename InType,
cpp::enable_if_t<cpp::is_floating_point_v<F> && cpp::is_integral_v<InType>,
int> = 0>
LIBC_INLINE InType round_to_signed_integer_using_current_rounding_mode(F x) {
return internal::rounded_float_to_signed_integer<F, InType>(
round_using_current_rounding_mode(x));
}

Expand Down
83 changes: 42 additions & 41 deletions libc/test/src/math/RoundToIntegerTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ using LIBC_NAMESPACE::Sign;
static constexpr int ROUNDING_MODES[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};

template <typename F, typename I, bool TestModes = false>
template <typename F, typename OutType, bool TestModes = false>
class RoundToIntegerTestTemplate
: public LIBC_NAMESPACE::testing::FEnvSafeTest {
public:
typedef I (*RoundToIntegerFunc)(F);
typedef OutType (*RoundToIntegerFunc)(F);

private:
using FPBits = LIBC_NAMESPACE::fputil::FPBits<F>;
Expand All @@ -49,10 +49,11 @@ class RoundToIntegerTestTemplate
static constexpr StorageType MIN_SUBNORMAL =
FPBits::min_subnormal().uintval();

static constexpr I INTEGER_MIN = I(1) << (sizeof(I) * 8 - 1);
static constexpr I INTEGER_MAX = -(INTEGER_MIN + 1);
static constexpr OutType INTEGER_MIN = OutType(1)
<< (sizeof(OutType) * 8 - 1);
static constexpr OutType INTEGER_MAX = -(INTEGER_MIN + 1);

void test_one_input(RoundToIntegerFunc func, F input, I expected,
void test_one_input(RoundToIntegerFunc func, F input, OutType expected,
bool expectError) {
LIBC_NAMESPACE::libc_errno = 0;
LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
Expand Down Expand Up @@ -120,24 +121,24 @@ class RoundToIntegerTestTemplate
}

void do_round_numbers_test(RoundToIntegerFunc func) {
test_one_input(func, zero, I(0), false);
test_one_input(func, neg_zero, I(0), false);
test_one_input(func, F(1.0), I(1), false);
test_one_input(func, F(-1.0), I(-1), false);
test_one_input(func, F(10.0), I(10), false);
test_one_input(func, F(-10.0), I(-10), false);
test_one_input(func, F(1234.0), I(1234), false);
test_one_input(func, F(-1234.0), I(-1234), false);
test_one_input(func, zero, OutType(0), false);
test_one_input(func, neg_zero, OutType(0), false);
test_one_input(func, F(1.0), OutType(1), false);
test_one_input(func, F(-1.0), OutType(-1), false);
test_one_input(func, F(10.0), OutType(10), false);
test_one_input(func, F(-10.0), OutType(-10), false);
test_one_input(func, F(1234.0), OutType(1234), false);
test_one_input(func, F(-1234.0), OutType(-1234), false);

// The rest of this function compares with an equivalent MPFR function
// which rounds floating point numbers to long values. There is no MPFR
// function to round to long long or wider integer values. So, we will
// the remaining tests only if the width of I less than equal to that of
// long.
if (sizeof(I) > sizeof(long))
// the remaining tests only if the width of OutType less than equal to that
// of long.
if (sizeof(OutType) > sizeof(long))
return;

constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1;
constexpr int EXPONENT_LIMIT = sizeof(OutType) * 8 - 1;
constexpr int BIASED_EXPONENT_LIMIT = EXPONENT_LIMIT + FPBits::EXP_BIAS;
if (BIASED_EXPONENT_LIMIT > FPBits::MAX_BIASED_EXPONENT)
return;
Expand Down Expand Up @@ -179,7 +180,7 @@ class RoundToIntegerTestTemplate
else
erangeflag = mpfr::round_to_long(x, mpfr_long_result);
ASSERT_FALSE(erangeflag);
I mpfr_result = mpfr_long_result;
OutType mpfr_result = mpfr_long_result;
test_one_input(func, x, mpfr_result, false);
}
}
Expand All @@ -201,12 +202,12 @@ class RoundToIntegerTestTemplate
// This function compares with an equivalent MPFR function which rounds
// floating point numbers to long values. There is no MPFR function to
// round to long long or wider integer values. So, we will peform the
// comparisons in this function only if the width of I less than equal to
// that of long.
if (sizeof(I) > sizeof(long))
// comparisons in this function only if the width of OutType less than equal
// to that of long.
if (sizeof(OutType) > sizeof(long))
return;

constexpr int EXPONENT_LIMIT = sizeof(I) * 8 - 1;
constexpr int EXPONENT_LIMIT = sizeof(OutType) * 8 - 1;
constexpr int BIASED_EXPONENT_LIMIT = EXPONENT_LIMIT + FPBits::EXP_BIAS;
if (BIASED_EXPONENT_LIMIT > FPBits::MAX_BIASED_EXPONENT)
return;
Expand Down Expand Up @@ -248,22 +249,22 @@ class RoundToIntegerTestTemplate
if (TestModes) {
if (x > 0) {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, I(1), false);
test_one_input(func, x, OutType(1), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
} else {
LIBC_NAMESPACE::fputil::set_round(FE_UPWARD);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_DOWNWARD);
test_one_input(func, x, I(-1), false);
test_one_input(func, x, OutType(-1), false);
LIBC_NAMESPACE::fputil::set_round(FE_TOWARDZERO);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
LIBC_NAMESPACE::fputil::set_round(FE_TONEAREST);
test_one_input(func, x, I(0), false);
test_one_input(func, x, OutType(0), false);
}
} else {
test_one_input(func, x, 0L, false);
Expand All @@ -275,9 +276,9 @@ class RoundToIntegerTestTemplate
// This function compares with an equivalent MPFR function which rounds
// floating point numbers to long values. There is no MPFR function to
// round to long long or wider integer values. So, we will peform the
// comparisons in this function only if the width of I less than equal to
// that of long.
if (sizeof(I) > sizeof(long))
// comparisons in this function only if the width of OutType less than equal
// to that of long.
if (sizeof(OutType) > sizeof(long))
return;

constexpr int COUNT = 1'000'001;
Expand All @@ -297,7 +298,7 @@ class RoundToIntegerTestTemplate
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, to_mpfr_rounding_mode(m),
mpfr_long_result);
I mpfr_result = mpfr_long_result;
OutType mpfr_result = mpfr_long_result;
LIBC_NAMESPACE::fputil::set_round(m);
if (erangeflag)
test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
Expand All @@ -307,7 +308,7 @@ class RoundToIntegerTestTemplate
} else {
long mpfr_long_result;
bool erangeflag = mpfr::round_to_long(x, mpfr_long_result);
I mpfr_result = mpfr_long_result;
OutType mpfr_result = mpfr_long_result;
if (erangeflag)
test_one_input(func, x, x > 0 ? INTEGER_MAX : INTEGER_MIN, true);
else
Expand All @@ -317,9 +318,9 @@ class RoundToIntegerTestTemplate
}
};

#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \
#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, OutType, func, TestModes) \
using LlvmLibcRoundToIntegerTest = \
RoundToIntegerTestTemplate<F, I, TestModes>; \
RoundToIntegerTestTemplate<F, OutType, TestModes>; \
TEST_F(LlvmLibcRoundToIntegerTest, InfinityAndNaN) { \
testInfinityAndNaN(&func); \
} \
Expand All @@ -335,10 +336,10 @@ class RoundToIntegerTestTemplate
} \
TEST_F(LlvmLibcRoundToIntegerTest, NormalRange) { testNormalRange(&func); }

#define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \
LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, false)
#define LIST_ROUND_TO_INTEGER_TESTS(F, OutType, func) \
LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, OutType, func, false)

#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, I, func) \
LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)
#define LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(F, OutType, func) \
LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, OutType, func, true)

#endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H
Loading