diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -123,12 +123,18 @@ libc.src.math.ldexp libc.src.math.ldexpf libc.src.math.ldexpl + libc.src.math.llrint + libc.src.math.llrintf + libc.src.math.llrintl libc.src.math.llround libc.src.math.llroundf libc.src.math.llroundl libc.src.math.logb libc.src.math.logbf libc.src.math.logbl + libc.src.math.lrint + libc.src.math.lrintf + libc.src.math.lrintl libc.src.math.lround libc.src.math.lroundf libc.src.math.lroundl @@ -141,6 +147,9 @@ libc.src.math.remquof libc.src.math.remquo libc.src.math.remquol + libc.src.math.rint + libc.src.math.rintf + libc.src.math.rintl libc.src.math.round libc.src.math.roundf libc.src.math.roundl diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -371,6 +371,18 @@ FunctionSpec<"llroundf", RetValSpec, [ArgSpec]>, FunctionSpec<"llroundl", RetValSpec, [ArgSpec]>, + FunctionSpec<"rint", RetValSpec, [ArgSpec]>, + FunctionSpec<"rintf", RetValSpec, [ArgSpec]>, + FunctionSpec<"rintl", RetValSpec, [ArgSpec]>, + + FunctionSpec<"lrint", RetValSpec, [ArgSpec]>, + FunctionSpec<"lrintf", RetValSpec, [ArgSpec]>, + FunctionSpec<"lrintl", RetValSpec, [ArgSpec]>, + + FunctionSpec<"llrint", RetValSpec, [ArgSpec]>, + FunctionSpec<"llrintf", RetValSpec, [ArgSpec]>, + FunctionSpec<"llrintl", RetValSpec, [ArgSpec]>, + FunctionSpec<"sqrt", RetValSpec, [ArgSpec]>, FunctionSpec<"sqrtf", RetValSpec, [ArgSpec]>, FunctionSpec<"sqrtl", RetValSpec, [ArgSpec]>, diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -308,6 +308,114 @@ -O2 ) +add_entrypoint_object( + rint + SRCS + rint.cpp + HDRS + rint.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + rintf + SRCS + rintf.cpp + HDRS + rintf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + rintl + SRCS + rintl.cpp + HDRS + rintl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + lrint + SRCS + lrint.cpp + HDRS + lrint.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + lrintf + SRCS + lrintf.cpp + HDRS + lrintf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + lrintl + SRCS + lrintl.cpp + HDRS + lrintl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llrint + SRCS + llrint.cpp + HDRS + llrint.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llrintf + SRCS + llrintf.cpp + HDRS + llrintf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llrintl + SRCS + llrintl.cpp + HDRS + llrintl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + add_object_library( exp_utils HDRS diff --git a/libc/src/math/llrint.h b/libc/src/math/llrint.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llrint.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llrint ------------------------*- 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_LLRINT_H +#define LLVM_LIBC_SRC_MATH_LLRINT_H + +namespace __llvm_libc { + +long long llrint(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLRINT_H diff --git a/libc/src/math/llrint.cpp b/libc/src/math/llrint.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llrint.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of llrint 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 long LLVM_LIBC_ENTRYPOINT(llrint)(double x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llrintf.h b/libc/src/math/llrintf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llrintf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llrintf -----------------------*- 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_LLRINTF_H +#define LLVM_LIBC_SRC_MATH_LLRINTF_H + +namespace __llvm_libc { + +long long llrintf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLRINTF_H diff --git a/libc/src/math/llrintf.cpp b/libc/src/math/llrintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llrintf.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of llrintf 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 long LLVM_LIBC_ENTRYPOINT(llrintf)(float x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode( + x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llrintl.h b/libc/src/math/llrintl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llrintl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llrintl -----------------------*- 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_LLRINTL_H +#define LLVM_LIBC_SRC_MATH_LLRINTL_H + +namespace __llvm_libc { + +long long llrintl(long double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLRINTL_H diff --git a/libc/src/math/llrintl.cpp b/libc/src/math/llrintl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llrintl.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of llrintl 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 long LLVM_LIBC_ENTRYPOINT(llrintl)(long double x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lrint.h b/libc/src/math/lrint.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lrint.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lrint -------------------------*- 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_LRINT_H +#define LLVM_LIBC_SRC_MATH_LRINT_H + +namespace __llvm_libc { + +long lrint(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LRINT_H diff --git a/libc/src/math/lrint.cpp b/libc/src/math/lrint.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lrint.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of lrint 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 LLVM_LIBC_ENTRYPOINT(lrint)(double x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lrintf.h b/libc/src/math/lrintf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lrintf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lrintf ------------------------*- 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_LRINTF_H +#define LLVM_LIBC_SRC_MATH_LRINTF_H + +namespace __llvm_libc { + +long lrintf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LRINTF_H diff --git a/libc/src/math/lrintf.cpp b/libc/src/math/lrintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lrintf.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of lrintf 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 LLVM_LIBC_ENTRYPOINT(lrintf)(float x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lrintl.h b/libc/src/math/lrintl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lrintl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lrintl ------------------------*- 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_LRINTL_H +#define LLVM_LIBC_SRC_MATH_LRINTL_H + +namespace __llvm_libc { + +long lrintl(long double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LRINTL_H diff --git a/libc/src/math/lrintl.cpp b/libc/src/math/lrintl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lrintl.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of lrintl 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 LLVM_LIBC_ENTRYPOINT(lrintl)(long double x) { + return fputil::roundToSignedIntegerUsingCurrentRoundingMode(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/rint.h b/libc/src/math/rint.h new file mode 100644 --- /dev/null +++ b/libc/src/math/rint.h @@ -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 diff --git a/libc/src/math/rint.cpp b/libc/src/math/rint.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/rint.cpp @@ -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 diff --git a/libc/src/math/rintf.h b/libc/src/math/rintf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/rintf.h @@ -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 diff --git a/libc/src/math/rintf.cpp b/libc/src/math/rintf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/rintf.cpp @@ -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 diff --git a/libc/src/math/rintl.h b/libc/src/math/rintl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/rintl.h @@ -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 diff --git a/libc/src/math/rintl.cpp b/libc/src/math/rintl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/rintl.cpp @@ -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 diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -390,6 +390,141 @@ 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 diff --git a/libc/test/src/math/RIntTest.h b/libc/test/src/math/RIntTest.h new file mode 100644 --- /dev/null +++ b/libc/test/src/math/RIntTest.h @@ -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 +#include +#include + +namespace mpfr = __llvm_libc::testing::mpfr; + +static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, + FE_TONEAREST}; + +template +class RIntTestTemplate : public __llvm_libc::testing::Test { +public: + typedef T (*RIntFunc)(T); + +private: + using FPBits = __llvm_libc::fputil::FPBits; + 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; \ + 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 diff --git a/libc/test/src/math/RoundToIntegerTest.h b/libc/test/src/math/RoundToIntegerTest.h --- a/libc/test/src/math/RoundToIntegerTest.h +++ b/libc/test/src/math/RoundToIntegerTest.h @@ -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" @@ -27,7 +24,10 @@ namespace mpfr = __llvm_libc::testing::mpfr; -template +static constexpr int roundingModes[4] = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, + FE_TONEAREST}; + +template class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test { public: typedef I (*RoundToIntegerFunc)(F); @@ -50,21 +50,21 @@ 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); @@ -72,6 +72,21 @@ } } + 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 @@ -82,13 +97,24 @@ #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); @@ -121,13 +147,44 @@ 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) { @@ -149,29 +206,56 @@ << (__llvm_libc::fputil::MantissaWidth::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); + } } } @@ -194,18 +278,33 @@ 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; \ +#define LIST_ROUND_TO_INTEGER_TESTS_HELPER(F, I, func, TestModes) \ + using RoundToIntegerTest = RoundToIntegerTestTemplate; \ TEST_F(RoundToIntegerTest, InfinityAndNaN) { testInfinityAndNaN(&func); } \ TEST_F(RoundToIntegerTest, RoundNumbers) { testRoundNumbers(&func); } \ TEST_F(RoundToIntegerTest, Fractions) { testFractions(&func); } \ @@ -213,4 +312,10 @@ 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 diff --git a/libc/test/src/math/llrint_test.cpp b/libc/test/src/math/llrint_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llrint_test.cpp @@ -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) diff --git a/libc/test/src/math/llrintf_test.cpp b/libc/test/src/math/llrintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llrintf_test.cpp @@ -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) diff --git a/libc/test/src/math/llrintl_test.cpp b/libc/test/src/math/llrintl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llrintl_test.cpp @@ -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) diff --git a/libc/test/src/math/lrint_test.cpp b/libc/test/src/math/lrint_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lrint_test.cpp @@ -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) diff --git a/libc/test/src/math/lrintf_test.cpp b/libc/test/src/math/lrintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lrintf_test.cpp @@ -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) diff --git a/libc/test/src/math/lrintl_test.cpp b/libc/test/src/math/lrintl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lrintl_test.cpp @@ -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) diff --git a/libc/test/src/math/rint_test.cpp b/libc/test/src/math/rint_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/rint_test.cpp @@ -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) diff --git a/libc/test/src/math/rintf_test.cpp b/libc/test/src/math/rintf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/rintf_test.cpp @@ -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) diff --git a/libc/test/src/math/rintl_test.cpp b/libc/test/src/math/rintl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/rintl_test.cpp @@ -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) diff --git a/libc/utils/FPUtil/NearestIntegerOperations.h b/libc/utils/FPUtil/NearestIntegerOperations.h --- a/libc/utils/FPUtil/NearestIntegerOperations.h +++ b/libc/utils/FPUtil/NearestIntegerOperations.h @@ -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 -#if math_errhandling & MATH_ERREXCEPT -#include "FEnv.h" -#endif #if math_errhandling & MATH_ERRNO #include "src/errno/llvmlibc_errno.h" #include @@ -159,17 +157,94 @@ } } +template ::Value, int> = 0> +static inline T roundUsingCurrentRoundingMode(T x) { + using UIntType = typename FPBits::UIntType; + FPBits 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(MantissaWidth::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::value - exponent; + FPBits 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 ::Value && cpp::IsIntegral::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 roundedValue = round(x); - FPBits bits(roundedValue); + FPBits bits(x); auto setDomainErrorAndRaiseInvalid = []() { #if math_errhandling & MATH_ERRNO llvmlibc_errno = EDOM; @@ -180,8 +255,6 @@ }; if (bits.isInfOrNaN()) { - // Result of round is infinity or NaN only if x is infinity - // or NaN. setDomainErrorAndRaiseInvalid(); return bits.sign ? IntegerMin : IntegerMax; } @@ -200,10 +273,29 @@ // 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 ::Value && + cpp::IsIntegral::Value, + int> = 0> +static inline I roundToSignedInteger(F x) { + return internal::roundedFloatToSignedInteger(round(x)); +} + +template ::Value && + cpp::IsIntegral::Value, + int> = 0> +static inline I roundToSignedIntegerUsingCurrentRoundingMode(F x) { + return internal::roundedFloatToSignedInteger( + roundUsingCurrentRoundingMode(x)); } } // namespace fputil diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -237,7 +237,12 @@ return internal::MPFRMatcher(input, t); } +enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest }; + +template T Round(T x, RoundingMode mode); + template bool RoundToLong(T x, long &result); +template bool RoundToLong(T x, RoundingMode mode, long &result); } // namespace mpfr } // namespace testing diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -163,6 +163,18 @@ 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); @@ -563,6 +575,23 @@ template bool compareBinaryOperationOneOutput( Operation, const BinaryInput &, 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 bool RoundToLong(T x, long &result) { @@ -574,6 +603,25 @@ template bool RoundToLong(double, long &); template bool RoundToLong(long double, long &); +template bool RoundToLong(T x, RoundingMode mode, long &result) { + MPFRNumber mpfr(x); + return mpfr.roundToLong(internal::getMPFRRoundingMode(mode), result); +} + +template bool RoundToLong(float, RoundingMode, long &); +template bool RoundToLong(double, RoundingMode, long &); +template bool RoundToLong(long double, RoundingMode, long &); + +template T Round(T x, RoundingMode mode) { + MPFRNumber mpfr(x); + MPFRNumber result = mpfr.rint(internal::getMPFRRoundingMode(mode)); + return result.as(); +} + +template float Round(float, RoundingMode); +template double Round(double, RoundingMode); +template long double Round(long double, RoundingMode); + } // namespace mpfr } // namespace testing } // namespace __llvm_libc