18 changes: 18 additions & 0 deletions libc/src/math/rint.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation of rint function -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/common.h"
#include "utils/FPUtil/NearestIntegerOperations.h"

namespace __llvm_libc {

double LLVM_LIBC_ENTRYPOINT(rint)(double x) {
return fputil::roundUsingCurrentRoundingMode(x);
}

} // namespace __llvm_libc
18 changes: 18 additions & 0 deletions libc/src/math/rint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for rint --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_RINT_H
#define LLVM_LIBC_SRC_MATH_RINT_H

namespace __llvm_libc {

double rint(double x);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_RINT_H
18 changes: 18 additions & 0 deletions libc/src/math/rintf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation of rintf function ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/common.h"
#include "utils/FPUtil/NearestIntegerOperations.h"

namespace __llvm_libc {

float LLVM_LIBC_ENTRYPOINT(rintf)(float x) {
return fputil::roundUsingCurrentRoundingMode(x);
}

} // namespace __llvm_libc
18 changes: 18 additions & 0 deletions libc/src/math/rintf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for rintf -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_RINTF_H
#define LLVM_LIBC_SRC_MATH_RINTF_H

namespace __llvm_libc {

float rintf(float x);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_RINTF_H
18 changes: 18 additions & 0 deletions libc/src/math/rintl.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation of rintl function ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/__support/common.h"
#include "utils/FPUtil/NearestIntegerOperations.h"

namespace __llvm_libc {

long double LLVM_LIBC_ENTRYPOINT(rintl)(long double x) {
return fputil::roundUsingCurrentRoundingMode(x);
}

} // namespace __llvm_libc
18 changes: 18 additions & 0 deletions libc/src/math/rintl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//===-- Implementation header for rintl -------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_MATH_RINTL_H
#define LLVM_LIBC_SRC_MATH_RINTL_H

namespace __llvm_libc {

long double rintl(long double x);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_MATH_RINTL_H
135 changes: 135 additions & 0 deletions libc/test/src/math/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,141 @@ add_fp_unittest(
libc.utils.FPUtil.fputil
)

add_fp_unittest(
rint_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
rint_test.cpp
HDRS
RIntTest.h
DEPENDS
libc.include.math
libc.src.math.rint
libc.utils.FPUtil.fputil
)

add_fp_unittest(
rintf_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
rintf_test.cpp
HDRS
RIntTest.h
DEPENDS
libc.include.math
libc.src.math.rintf
libc.utils.FPUtil.fputil
)

add_fp_unittest(
rintl_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
rintl_test.cpp
HDRS
RIntTest.h
DEPENDS
libc.include.math
libc.src.math.rintl
libc.utils.FPUtil.fputil
)

add_fp_unittest(
lrint_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
lrint_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.lrint
libc.utils.FPUtil.fputil
)

add_fp_unittest(
lrintf_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
lrintf_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.lrintf
libc.utils.FPUtil.fputil
)

add_fp_unittest(
lrintl_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
lrintl_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.lrintl
libc.utils.FPUtil.fputil
)

add_fp_unittest(
llrint_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
llrint_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.llrint
libc.utils.FPUtil.fputil
)

add_fp_unittest(
llrintf_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
llrintf_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.llrintf
libc.utils.FPUtil.fputil
)

add_fp_unittest(
llrintl_test
NEED_MPFR
SUITE
libc_math_unittests
SRCS
llrintl_test.cpp
HDRS
RoundToIntegerTest.h
DEPENDS
libc.include.math
libc.src.math.llrintl
libc.utils.FPUtil.fputil
)

add_fp_unittest(
expf_test
NEED_MPFR
Expand Down
138 changes: 138 additions & 0 deletions libc/test/src/math/RIntTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//===-- Utility class to test different flavors of rint ---------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_TEST_SRC_MATH_RINTTEST_H
#define LLVM_LIBC_TEST_SRC_MATH_RINTTEST_H

#include "utils/FPUtil/FEnv.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/FPUtil/TestHelpers.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/Test.h"

#include <fenv.h>
#include <math.h>
#include <stdio.h>

namespace mpfr = __llvm_libc::testing::mpfr;

static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};

template <typename T>
class RIntTestTemplate : public __llvm_libc::testing::Test {
public:
typedef T (*RIntFunc)(T);

private:
using FPBits = __llvm_libc::fputil::FPBits<T>;
using UIntType = typename FPBits::UIntType;

const T zero = FPBits::zero();
const T negZero = FPBits::negZero();
const T inf = FPBits::inf();
const T negInf = FPBits::negInf();
const T nan = FPBits::buildNaN(1);

static inline mpfr::RoundingMode toMPFRRoundingMode(int mode) {
switch (mode) {
case FE_UPWARD:
return mpfr::RoundingMode::Upward;
case FE_DOWNWARD:
return mpfr::RoundingMode::Downward;
case FE_TOWARDZERO:
return mpfr::RoundingMode::TowardZero;
case FE_TONEAREST:
return mpfr::RoundingMode::Nearest;
default:
__builtin_unreachable();
}
}

public:
void testSpecialNumbers(RIntFunc func) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
ASSERT_FP_EQ(inf, func(inf));
ASSERT_FP_EQ(negInf, func(negInf));
ASSERT_FP_EQ(nan, func(nan));
ASSERT_FP_EQ(zero, func(zero));
ASSERT_FP_EQ(negZero, func(negZero));
}
}

void testRoundNumbers(RIntFunc func) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
mpfr::RoundingMode mpfrMode = toMPFRRoundingMode(mode);
ASSERT_FP_EQ(func(T(1.0)), mpfr::Round(T(1.0), mpfrMode));
ASSERT_FP_EQ(func(T(-1.0)), mpfr::Round(T(-1.0), mpfrMode));
ASSERT_FP_EQ(func(T(10.0)), mpfr::Round(T(10.0), mpfrMode));
ASSERT_FP_EQ(func(T(-10.0)), mpfr::Round(T(-10.0), mpfrMode));
ASSERT_FP_EQ(func(T(1234.0)), mpfr::Round(T(1234.0), mpfrMode));
ASSERT_FP_EQ(func(T(-1234.0)), mpfr::Round(T(-1234.0), mpfrMode));
}
}

void testFractions(RIntFunc func) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
mpfr::RoundingMode mpfrMode = toMPFRRoundingMode(mode);
ASSERT_FP_EQ(func(T(0.5)), mpfr::Round(T(0.5), mpfrMode));
ASSERT_FP_EQ(func(T(-0.5)), mpfr::Round(T(-0.5), mpfrMode));
ASSERT_FP_EQ(func(T(0.115)), mpfr::Round(T(0.115), mpfrMode));
ASSERT_FP_EQ(func(T(-0.115)), mpfr::Round(T(-0.115), mpfrMode));
ASSERT_FP_EQ(func(T(0.715)), mpfr::Round(T(0.715), mpfrMode));
ASSERT_FP_EQ(func(T(-0.715)), mpfr::Round(T(-0.715), mpfrMode));
}
}

void testSubnormalRange(RIntFunc func) {
constexpr UIntType count = 1000001;
constexpr UIntType step =
(FPBits::maxSubnormal - FPBits::minSubnormal) / count;
for (UIntType i = FPBits::minSubnormal; i <= FPBits::maxSubnormal;
i += step) {
T x = FPBits(i);
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
mpfr::RoundingMode mpfrMode = toMPFRRoundingMode(mode);
ASSERT_FP_EQ(func(x), mpfr::Round(x, mpfrMode));
}
}
}

void testNormalRange(RIntFunc func) {
constexpr UIntType count = 1000001;
constexpr UIntType step = (FPBits::maxNormal - FPBits::minNormal) / count;
for (UIntType i = FPBits::minNormal; i <= FPBits::maxNormal; i += step) {
T x = FPBits(i);
// In normal range on x86 platforms, the long double implicit 1 bit can be
// zero making the numbers NaN. We will skip them.
if (isnan(x)) {
continue;
}

for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
mpfr::RoundingMode mpfrMode = toMPFRRoundingMode(mode);
ASSERT_FP_EQ(func(x), mpfr::Round(x, mpfrMode));
}
}
}
};

#define LIST_RINT_TESTS(F, func) \
using RIntTest = RIntTestTemplate<F>; \
TEST_F(RIntTest, specialNumbers) { testSpecialNumbers(&func); } \
TEST_F(RIntTest, RoundNumbers) { testRoundNumbers(&func); } \
TEST_F(RIntTest, Fractions) { testFractions(&func); } \
TEST_F(RIntTest, SubnormalRange) { testSubnormalRange(&func); } \
TEST_F(RIntTest, NormalRange) { testNormalRange(&func); }

#endif // LLVM_LIBC_TEST_SRC_MATH_RINTTEST_H
177 changes: 141 additions & 36 deletions libc/test/src/math/RoundToIntegerTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@
#define LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H

#include "src/errno/llvmlibc_errno.h"
#include "src/fenv/feclearexcept.h"
#include "src/fenv/feraiseexcept.h"
#include "src/fenv/fetestexcept.h"
#include "utils/FPUtil/FPBits.h"
#include "utils/MPFRWrapper/MPFRUtils.h"
#include "utils/UnitTest/Test.h"
Expand All @@ -27,7 +24,10 @@

namespace mpfr = __llvm_libc::testing::mpfr;

template <typename F, typename I>
static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO,
FE_TONEAREST};

template <typename F, typename I, bool TestModes = false>
class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
public:
typedef I (*RoundToIntegerFunc)(F);
Expand All @@ -50,28 +50,43 @@ class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
llvmlibc_errno = 0;
#endif
#if math_errhandling & MATH_ERREXCEPT
__llvm_libc::feclearexcept(FE_ALL_EXCEPT);
__llvm_libc::fputil::clearExcept(FE_ALL_EXCEPT);
#endif

ASSERT_EQ(func(input), expected);

if (expectError) {
#if math_errhandling & MATH_ERREXCEPT
ASSERT_EQ(__llvm_libc::fetestexcept(FE_ALL_EXCEPT), FE_INVALID);
ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT), FE_INVALID);
#endif
#if math_errhandling & MATH_ERRNO
ASSERT_EQ(llvmlibc_errno, EDOM);
#endif
} else {
#if math_errhandling & MATH_ERREXCEPT
ASSERT_EQ(__llvm_libc::fetestexcept(FE_ALL_EXCEPT), 0);
ASSERT_EQ(__llvm_libc::fputil::testExcept(FE_ALL_EXCEPT), 0);
#endif
#if math_errhandling & MATH_ERRNO
ASSERT_EQ(llvmlibc_errno, 0);
#endif
}
}

static inline mpfr::RoundingMode toMPFRRoundingMode(int mode) {
switch (mode) {
case FE_UPWARD:
return mpfr::RoundingMode::Upward;
case FE_DOWNWARD:
return mpfr::RoundingMode::Downward;
case FE_TOWARDZERO:
return mpfr::RoundingMode::TowardZero;
case FE_TONEAREST:
return mpfr::RoundingMode::Nearest;
default:
__builtin_unreachable();
}
}

public:
void SetUp() override {
#if math_errhandling & MATH_ERREXCEPT
Expand All @@ -82,13 +97,24 @@ class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
#endif
}

void testInfinityAndNaN(RoundToIntegerFunc func) {
void doInfinityAndNaNTest(RoundToIntegerFunc func) {
testOneInput(func, inf, IntegerMax, true);
testOneInput(func, negInf, IntegerMin, true);
testOneInput(func, nan, IntegerMax, true);
}

void testRoundNumbers(RoundToIntegerFunc func) {
void testInfinityAndNaN(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
doInfinityAndNaNTest(func);
}
} else {
doInfinityAndNaNTest(func);
}
}

void doRoundNumbersTest(RoundToIntegerFunc func) {
testOneInput(func, zero, I(0), false);
testOneInput(func, negZero, I(0), false);
testOneInput(func, F(1.0), I(1), false);
Expand Down Expand Up @@ -121,13 +147,44 @@ class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
testOneInput(func, x, mpfrResult, false);
}

void testRoundNumbers(RoundToIntegerFunc func) {
if (TestModes) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
doRoundNumbersTest(func);
}
} else {
doRoundNumbersTest(func);
}
}

void doFractionsTest(RoundToIntegerFunc func, int mode) {
constexpr F fractions[] = {0.5, -0.5, 0.115, -0.115, 0.715, -0.715};
for (F x : fractions) {
long mpfrLongResult;
bool erangeflag;
if (TestModes)
erangeflag =
mpfr::RoundToLong(x, toMPFRRoundingMode(mode), mpfrLongResult);
else
erangeflag = mpfr::RoundToLong(x, mpfrLongResult);
ASSERT_FALSE(erangeflag);
I mpfrResult = mpfrLongResult;
testOneInput(func, x, mpfrResult, false);
}
}

void testFractions(RoundToIntegerFunc func) {
testOneInput(func, F(0.5), I(1), false);
testOneInput(func, F(-0.5), I(-1), false);
testOneInput(func, F(0.115), I(0), false);
testOneInput(func, F(-0.115), I(0), false);
testOneInput(func, F(0.715), I(1), false);
testOneInput(func, F(-0.715), I(-1), false);
if (TestModes) {
for (int mode : roundingModes) {
__llvm_libc::fputil::setRound(mode);
doFractionsTest(func, mode);
}
} else {
// Passing 0 for mode has no effect as it is not used in doFractionsTest
// when `TestModes` is false;
doFractionsTest(func, 0);
}
}

void testIntegerOverflow(RoundToIntegerFunc func) {
Expand All @@ -149,29 +206,56 @@ class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
<< (__llvm_libc::fputil::MantissaWidth<F>::value - 1);

F x = bits;
long mpfrResult;
bool erangeflag = mpfr::RoundToLong(x, mpfrResult);
ASSERT_TRUE(erangeflag);
testOneInput(func, x, IntegerMin, true);
if (TestModes) {
for (int m : roundingModes) {
__llvm_libc::fputil::setRound(m);
long mpfrLongResult;
bool erangeflag =
mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult);
ASSERT_TRUE(erangeflag);
testOneInput(func, x, IntegerMin, true);
}
} else {
long mpfrLongResult;
bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult);
ASSERT_TRUE(erangeflag);
testOneInput(func, x, IntegerMin, true);
}
}

void testSubnormalRange(RoundToIntegerFunc func) {
// 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))
return;

constexpr UIntType count = 1000001;
constexpr UIntType step =
(FPBits::maxSubnormal - FPBits::minSubnormal) / count;
for (UIntType i = FPBits::minSubnormal; i <= FPBits::maxSubnormal;
i += step) {
F x = FPBits(i);
if (x == F(0.0))
continue;
// All subnormal numbers should round to zero.
testOneInput(func, x, 0L, false);
if (TestModes) {
if (x > 0) {
__llvm_libc::fputil::setRound(FE_UPWARD);
testOneInput(func, x, I(1), false);
__llvm_libc::fputil::setRound(FE_DOWNWARD);
testOneInput(func, x, I(0), false);
__llvm_libc::fputil::setRound(FE_TOWARDZERO);
testOneInput(func, x, I(0), false);
__llvm_libc::fputil::setRound(FE_TONEAREST);
testOneInput(func, x, I(0), false);
} else {
__llvm_libc::fputil::setRound(FE_UPWARD);
testOneInput(func, x, I(0), false);
__llvm_libc::fputil::setRound(FE_DOWNWARD);
testOneInput(func, x, I(-1), false);
__llvm_libc::fputil::setRound(FE_TOWARDZERO);
testOneInput(func, x, I(0), false);
__llvm_libc::fputil::setRound(FE_TONEAREST);
testOneInput(func, x, I(0), false);
}
} else {
testOneInput(func, x, 0L, false);
}
}
}

Expand All @@ -194,23 +278,44 @@ class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test {
continue;
}

long mpfrResult;
bool erangeflag = mpfr::RoundToLong(x, mpfrResult);
if (erangeflag)
testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true);
else
testOneInput(func, x, mpfrResult, false);
if (TestModes) {
for (int m : roundingModes) {
long mpfrLongResult;
bool erangeflag =
mpfr::RoundToLong(x, toMPFRRoundingMode(m), mpfrLongResult);
I mpfrResult = mpfrLongResult;
__llvm_libc::fputil::setRound(m);
if (erangeflag)
testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true);
else
testOneInput(func, x, mpfrResult, false);
}
} else {
long mpfrLongResult;
bool erangeflag = mpfr::RoundToLong(x, mpfrLongResult);
I mpfrResult = mpfrLongResult;
if (erangeflag)
testOneInput(func, x, x > 0 ? IntegerMax : IntegerMin, true);
else
testOneInput(func, x, mpfrResult, false);
}
}
}
};

#define LIST_ROUND_TO_INTEGER_TESTS(F, I, func) \
using RoundToIntegerTest = RoundToIntegerTestTemplate<F, I>; \
#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \
using RoundToIntegerTest = RoundToIntegerTestTemplate<F, I, TestModes>; \
TEST_F(RoundToIntegerTest, InfinityAndNaN) { testInfinityAndNaN(&func); } \
TEST_F(RoundToIntegerTest, RoundNumbers) { testRoundNumbers(&func); } \
TEST_F(RoundToIntegerTest, Fractions) { testFractions(&func); } \
TEST_F(RoundToIntegerTest, IntegerOverflow) { testIntegerOverflow(&func); } \
TEST_F(RoundToIntegerTest, SubnormalRange) { testSubnormalRange(&func); } \
TEST_F(RoundToIntegerTest, 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_WITH_MODES(F, I, func) \
LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, true)

#endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H
13 changes: 13 additions & 0 deletions libc/test/src/math/llrint_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for llrint ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/llrint.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(double, long long, __llvm_libc::llrint)
13 changes: 13 additions & 0 deletions libc/test/src/math/llrintf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for llrintf ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/llrintf.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(float, long long, __llvm_libc::llrintf)
14 changes: 14 additions & 0 deletions libc/test/src/math/llrintl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//===-- Unittests for llrintl ---------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/llrintl.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(long double, long long,
__llvm_libc::llrintl)
13 changes: 13 additions & 0 deletions libc/test/src/math/lrint_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for lrint -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/lrint.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(double, long, __llvm_libc::lrint)
13 changes: 13 additions & 0 deletions libc/test/src/math/lrintf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for lrintf ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/lrintf.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(float, long, __llvm_libc::lrintf)
13 changes: 13 additions & 0 deletions libc/test/src/math/lrintl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for lrintl ----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RoundToIntegerTest.h"

#include "src/math/lrintl.h"

LIST_ROUND_TO_INTEGER_TESTS_WITH_MODES(long double, long, __llvm_libc::lrintl)
13 changes: 13 additions & 0 deletions libc/test/src/math/rint_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for rint ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RIntTest.h"

#include "src/math/rint.h"

LIST_RINT_TESTS(double, __llvm_libc::rint)
13 changes: 13 additions & 0 deletions libc/test/src/math/rintf_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for rintf -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RIntTest.h"

#include "src/math/rintf.h"

LIST_RINT_TESTS(float, __llvm_libc::rintf)
13 changes: 13 additions & 0 deletions libc/test/src/math/rintl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//===-- Unittests for rintl -----------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "RIntTest.h"

#include "src/math/rintl.h"

LIST_RINT_TESTS(long double, __llvm_libc::rintl)
118 changes: 105 additions & 13 deletions libc/utils/FPUtil/NearestIntegerOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@
#ifndef LLVM_LIBC_UTILS_FPUTIL_NEAREST_INTEGER_OPERATIONS_H
#define LLVM_LIBC_UTILS_FPUTIL_NEAREST_INTEGER_OPERATIONS_H

#include "FEnv.h"
#include "FPBits.h"

#include "utils/CPP/TypeTraits.h"

#include <math.h>
#if math_errhandling & MATH_ERREXCEPT
#include "FEnv.h"
#endif
#if math_errhandling & MATH_ERRNO
#include "src/errno/llvmlibc_errno.h"
#include <errno.h>
Expand Down Expand Up @@ -159,17 +157,94 @@ static inline T round(T x) {
}
}

template <typename T,
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
static inline T roundUsingCurrentRoundingMode(T x) {
using UIntType = typename FPBits<T>::UIntType;
FPBits<T> bits(x);

// If x is infinity NaN or zero, return it.
if (bits.isInfOrNaN() || bits.isZero())
return x;

bool isNeg = bits.sign;
int exponent = bits.getExponent();
int roundingMode = getRound();

// If the exponent is greater than the most negative mantissa
// exponent, then x is already an integer.
if (exponent >= static_cast<int>(MantissaWidth<T>::value))
return x;

if (exponent <= -1) {
switch (roundingMode) {
case FE_DOWNWARD:
return isNeg ? T(-1.0) : T(0.0);
case FE_UPWARD:
return isNeg ? T(-0.0) : T(1.0);
case FE_TOWARDZERO:
return isNeg ? T(-0.0) : T(0.0);
case FE_TONEAREST:
if (exponent <= -2 || bits.mantissa == 0)
return isNeg ? T(-0.0) : T(0.0); // abs(x) <= 0.5
else
return isNeg ? T(-1.0) : T(1.0); // abs(x) > 0.5
default:
__builtin_unreachable();
}
}

uint32_t trimSize = MantissaWidth<T>::value - exponent;
FPBits<T> newBits = bits;
newBits.mantissa = (bits.mantissa >> trimSize) << trimSize;
T truncValue = T(newBits);

// If x is already an integer, return it.
if (truncValue == x)
return x;

UIntType trimValue = bits.mantissa & ((UIntType(1) << trimSize) - 1);
UIntType halfValue = (UIntType(1) << (trimSize - 1));
// If exponent is 0, trimSize will be equal to the mantissa width, and
// truncIsOdd` will not be correct. So, we handle it as a special case
// below.
UIntType truncIsOdd = newBits.mantissa & (UIntType(1) << trimSize);

switch (roundingMode) {
case FE_DOWNWARD:
return isNeg ? truncValue - T(1.0) : truncValue;
case FE_UPWARD:
return isNeg ? truncValue : truncValue + T(1.0);
case FE_TOWARDZERO:
return truncValue;
case FE_TONEAREST:
if (trimValue > halfValue) {
return isNeg ? truncValue - T(1.0) : truncValue + T(1.0);
} else if (trimValue == halfValue) {
if (exponent == 0)
return isNeg ? T(-2.0) : T(2.0);
if (truncIsOdd)
return isNeg ? truncValue - T(1.0) : truncValue + T(1.0);
else
return truncValue;
} else {
return truncValue;
}
default:
__builtin_unreachable();
}
}

namespace internal {

template <typename F, typename I,
cpp::EnableIfType<cpp::IsFloatingPointType<F>::Value &&
cpp::IsIntegral<I>::Value,
int> = 0>
static inline I roundToSignedInteger(F x) {
static inline I roundedFloatToSignedInteger(F x) {
constexpr I IntegerMin = (I(1) << (sizeof(I) * 8 - 1));
constexpr I IntegerMax = -(IntegerMin + 1);

using FPBits = FPBits<F>;
F roundedValue = round(x);
FPBits bits(roundedValue);
FPBits<F> bits(x);
auto setDomainErrorAndRaiseInvalid = []() {
#if math_errhandling & MATH_ERRNO
llvmlibc_errno = EDOM;
Expand All @@ -180,8 +255,6 @@ static inline I roundToSignedInteger(F x) {
};

if (bits.isInfOrNaN()) {
// Result of round is infinity or NaN only if x is infinity
// or NaN.
setDomainErrorAndRaiseInvalid();
return bits.sign ? IntegerMin : IntegerMax;
}
Expand All @@ -200,10 +273,29 @@ static inline I roundToSignedInteger(F x) {
// value is the most negative number for the signed integer type I.
}

// For all other cases, if |roundedValue| can fit in the integer type |I|,
// we just return the roundedValue. Implicit conversion will convert the
// For all other cases, if `x` can fit in the integer type `I`,
// we just return `x`. Implicit conversion will convert the
// floating point value to the exact integer value.
return roundedValue;
return x;
}

} // namespace internal

template <typename F, typename I,
cpp::EnableIfType<cpp::IsFloatingPointType<F>::Value &&
cpp::IsIntegral<I>::Value,
int> = 0>
static inline I roundToSignedInteger(F x) {
return internal::roundedFloatToSignedInteger<F, I>(round(x));
}

template <typename F, typename I,
cpp::EnableIfType<cpp::IsFloatingPointType<F>::Value &&
cpp::IsIntegral<I>::Value,
int> = 0>
static inline I roundToSignedIntegerUsingCurrentRoundingMode(F x) {
return internal::roundedFloatToSignedInteger<F, I>(
roundUsingCurrentRoundingMode(x));
}

} // namespace fputil
Expand Down
48 changes: 48 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,18 @@ class MPFRNumber {
return mpfr_erangeflag_p();
}

bool roundToLong(mpfr_rnd_t rnd, long &result) const {
MPFRNumber rint_result;
mpfr_rint(rint_result.value, value, rnd);
return rint_result.roundToLong(result);
}

MPFRNumber rint(mpfr_rnd_t rnd) const {
MPFRNumber result;
mpfr_rint(result.value, value, rnd);
return result;
}

MPFRNumber sin() const {
MPFRNumber result;
mpfr_sin(result.value, value, MPFR_RNDN);
Expand Down Expand Up @@ -563,6 +575,23 @@ compareBinaryOperationOneOutput<double>(Operation, const BinaryInput<double> &,
template bool compareBinaryOperationOneOutput<long double>(
Operation, const BinaryInput<long double> &, long double, double);

static mpfr_rnd_t getMPFRRoundingMode(RoundingMode mode) {
switch (mode) {
case RoundingMode::Upward:
return MPFR_RNDU;
break;
case RoundingMode::Downward:
return MPFR_RNDD;
break;
case RoundingMode::TowardZero:
return MPFR_RNDZ;
break;
case RoundingMode::Nearest:
return MPFR_RNDN;
break;
}
}

} // namespace internal

template <typename T> bool RoundToLong(T x, long &result) {
Expand All @@ -574,6 +603,25 @@ template bool RoundToLong<float>(float, long &);
template bool RoundToLong<double>(double, long &);
template bool RoundToLong<long double>(long double, long &);

template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result) {
MPFRNumber mpfr(x);
return mpfr.roundToLong(internal::getMPFRRoundingMode(mode), result);
}

template bool RoundToLong<float>(float, RoundingMode, long &);
template bool RoundToLong<double>(double, RoundingMode, long &);
template bool RoundToLong<long double>(long double, RoundingMode, long &);

template <typename T> T Round(T x, RoundingMode mode) {
MPFRNumber mpfr(x);
MPFRNumber result = mpfr.rint(internal::getMPFRRoundingMode(mode));
return result.as<T>();
}

template float Round<float>(float, RoundingMode);
template double Round<double>(double, RoundingMode);
template long double Round<long double>(long double, RoundingMode);

} // namespace mpfr
} // namespace testing
} // namespace __llvm_libc
5 changes: 5 additions & 0 deletions libc/utils/MPFRWrapper/MPFRUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,12 @@ getMPFRMatcher(InputType input, OutputType outputUnused, double t) {
return internal::MPFRMatcher<op, InputType, OutputType>(input, t);
}

enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest };

template <typename T> T Round(T x, RoundingMode mode);

template <typename T> bool RoundToLong(T x, long &result);
template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result);

} // namespace mpfr
} // namespace testing
Expand Down