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 @@ -122,9 +122,15 @@ libc.src.math.ldexp libc.src.math.ldexpf libc.src.math.ldexpl + 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.lround + libc.src.math.lroundf + libc.src.math.lroundl libc.src.math.modf libc.src.math.modff libc.src.math.modfl diff --git a/libc/spec/spec.td b/libc/spec/spec.td --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -39,11 +39,11 @@ def VarArgType : Type {} def VoidType : NamedType<"void">; def IntType : NamedType<"int">; +def LongType : NamedType<"long">; +def LongLongType : NamedType<"long long">; def FloatType : NamedType<"float">; def DoubleType : NamedType<"double">; def LongDoubleType : NamedType<"long double">; -def LongLongType : NamedType<"long long">; -def LongType : NamedType<"long">; def CharType : NamedType<"char">; // Common types diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -363,6 +363,14 @@ FunctionSpec<"roundf", RetValSpec, [ArgSpec]>, FunctionSpec<"roundl", RetValSpec, [ArgSpec]>, + FunctionSpec<"lround", RetValSpec, [ArgSpec]>, + FunctionSpec<"lroundf", RetValSpec, [ArgSpec]>, + FunctionSpec<"lroundl", RetValSpec, [ArgSpec]>, + + FunctionSpec<"llround", RetValSpec, [ArgSpec]>, + FunctionSpec<"llroundf", RetValSpec, [ArgSpec]>, + FunctionSpec<"llroundl", 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 @@ -236,6 +236,78 @@ -O2 ) +add_entrypoint_object( + lround + SRCS + lround.cpp + HDRS + lround.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + lroundf + SRCS + lroundf.cpp + HDRS + lroundf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + lroundl + SRCS + lroundl.cpp + HDRS + lroundl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llround + SRCS + llround.cpp + HDRS + llround.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llroundf + SRCS + llroundf.cpp + HDRS + llroundf.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + llroundl + SRCS + llroundl.cpp + HDRS + llroundl.h + DEPENDS + libc.utils.FPUtil.fputil + COMPILE_OPTIONS + -O2 +) + add_object_library( exp_utils HDRS diff --git a/libc/src/math/llround.h b/libc/src/math/llround.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llround.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llround -----------------------*- 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_LLROUND_H +#define LLVM_LIBC_SRC_MATH_LLROUND_H + +namespace __llvm_libc { + +long long llround(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLROUND_H diff --git a/libc/src/math/llround.cpp b/libc/src/math/llround.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llround.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of llround 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(llround)(double x) { + return fputil::roundToSignedInteger(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llroundf.h b/libc/src/math/llroundf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llroundf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llroundf ----------------------*- 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_LLROUNDF_H +#define LLVM_LIBC_SRC_MATH_LLROUNDF_H + +namespace __llvm_libc { + +long long llroundf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLROUNDF_H diff --git a/libc/src/math/llroundf.cpp b/libc/src/math/llroundf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llroundf.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of llroundf 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(llroundf)(float x) { + return fputil::roundToSignedInteger(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llroundl.h b/libc/src/math/llroundl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/llroundl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for llroundl ----------------------*- 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_LLROUNDL_H +#define LLVM_LIBC_SRC_MATH_LLROUNDL_H + +namespace __llvm_libc { + +long long llroundl(long double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LLROUNDL_H diff --git a/libc/src/math/llroundl.cpp b/libc/src/math/llroundl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/llroundl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of llroundl 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(llroundl)(long double x) { + return fputil::roundToSignedInteger(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lround.h b/libc/src/math/lround.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lround.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lround ------------------------*- 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_LROUND_H +#define LLVM_LIBC_SRC_MATH_LROUND_H + +namespace __llvm_libc { + +long lround(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LROUND_H diff --git a/libc/src/math/lround.cpp b/libc/src/math/lround.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lround.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of lround 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(lround)(double x) { + return fputil::roundToSignedInteger(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lroundf.h b/libc/src/math/lroundf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lroundf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lroundf -----------------------*- 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_LROUNDF_H +#define LLVM_LIBC_SRC_MATH_LROUNDF_H + +namespace __llvm_libc { + +long lroundf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LROUNDF_H diff --git a/libc/src/math/lroundf.cpp b/libc/src/math/lroundf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lroundf.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of lroundf 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(lroundf)(float x) { + return fputil::roundToSignedInteger(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lroundl.h b/libc/src/math/lroundl.h new file mode 100644 --- /dev/null +++ b/libc/src/math/lroundl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for lroundl -----------------------*- 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_LROUNDL_H +#define LLVM_LIBC_SRC_MATH_LROUNDL_H + +namespace __llvm_libc { + +long lroundl(long double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_LROUNDL_H diff --git a/libc/src/math/lroundl.cpp b/libc/src/math/lroundl.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/lroundl.cpp @@ -0,0 +1,18 @@ +//===-- Implementation of lroundl 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(lroundl)(long double x) { + return fputil::roundToSignedInteger(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 @@ -267,6 +267,126 @@ libc.utils.FPUtil.fputil ) +add_fp_unittest( + lround_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + lround_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.lround + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + lroundf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + lroundf_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.lroundf + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + lroundl_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + lroundl_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.lroundl + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + llround_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + llround_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.llround + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + llroundf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + llroundf_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.llroundf + libc.utils.FPUtil.fputil +) + +add_fp_unittest( + llroundl_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + llroundl_test.cpp + HDRS + RoundToIntegerTest.h + DEPENDS + libc.include.errno + libc.include.math + libc.src.errno.__errno_location + libc.src.fenv.feclearexcept + libc.src.fenv.feraiseexcept + libc.src.fenv.fetestexcept + libc.src.math.llroundl + libc.utils.FPUtil.fputil +) + add_fp_unittest( expf_test NEED_MPFR diff --git a/libc/test/src/math/RoundToIntegerTest.h b/libc/test/src/math/RoundToIntegerTest.h new file mode 100644 --- /dev/null +++ b/libc/test/src/math/RoundToIntegerTest.h @@ -0,0 +1,217 @@ +//===-- Utility class to test different flavors of [l|ll]round --*- 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_ROUNDTOINTEGERTEST_H +#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/CPP/TypeTraits.h" +#include "utils/FPUtil/FPBits.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" + +#include +#if math_errhandling & MATH_ERRNO +#include +#endif +#if math_errhandling & MATH_ERREXCEPT +#include "utils/FPUtil/FEnv.h" +#endif + +namespace mpfr = __llvm_libc::testing::mpfr; + +template +class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test { +public: + typedef I (*RoundToIntegerFunc)(F); + +private: + using FPBits = __llvm_libc::fputil::FPBits; + using UIntType = typename FPBits::UIntType; + + const F zero = __llvm_libc::fputil::FPBits::zero(); + const F negZero = __llvm_libc::fputil::FPBits::negZero(); + const F inf = __llvm_libc::fputil::FPBits::inf(); + const F negInf = __llvm_libc::fputil::FPBits::negInf(); + const F nan = __llvm_libc::fputil::FPBits::buildNaN(1); + static constexpr I IntegerMin = I(1) << (sizeof(I) * 8 - 1); + static constexpr I IntegerMax = -(IntegerMin + 1); + + void testOneInput(RoundToIntegerFunc func, F input, I expected, + bool expectError) { +#if math_errhandling & MATH_ERRNO + llvmlibc_errno = 0; +#endif +#if math_errhandling & MATH_ERREXCEPT + __llvm_libc::feclearexcept(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); +#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); +#endif +#if math_errhandling & MATH_ERRNO + ASSERT_EQ(llvmlibc_errno, 0); +#endif + } + } + +public: + void SetUp() override { +#if math_errhandling & MATH_ERREXCEPT + // We will disable all exceptions so that the test will not + // crash with SIGFPE. We can still use fetestexcept to check + // if the appropriate flag was raised. + __llvm_libc::fputil::disableExcept(FE_ALL_EXCEPT); +#endif + } + + void testInfinityAndNaN(RoundToIntegerFunc func) { + testOneInput(func, inf, IntegerMax, true); + testOneInput(func, negInf, IntegerMin, true); + testOneInput(func, nan, IntegerMax, true); + } + + void testRoundNumbers(RoundToIntegerFunc func) { + testOneInput(func, zero, I(0), false); + testOneInput(func, negZero, I(0), false); + testOneInput(func, F(1.0), I(1), false); + testOneInput(func, F(-1.0), I(-1), false); + testOneInput(func, F(10.0), I(10), false); + testOneInput(func, F(-10.0), I(-10), false); + testOneInput(func, F(1234.0), I(1234), false); + testOneInput(func, F(-1234.0), I(-1234), false); + + // The rest of this 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)) + return; + + constexpr int exponentLimit = sizeof(I) * 8 - 1; + // We start with 1.0 so that the implicit bit for x86 long doubles + // is set. + FPBits bits(F(1.0)); + bits.exponent = exponentLimit + FPBits::exponentBias; + bits.sign = 1; + bits.mantissa = 0; + + F x = bits; + long mpfrResult; + bool erangeflag = mpfr::RoundToLong(x, mpfrResult); + ASSERT_FALSE(erangeflag); + 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); + } + + void testIntegerOverflow(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 int exponentLimit = sizeof(I) * 8 - 1; + // We start with 1.0 so that the implicit bit for x86 long doubles + // is set. + FPBits bits(F(1.0)); + bits.exponent = exponentLimit + FPBits::exponentBias; + bits.sign = 1; + bits.mantissa = UIntType(0x1) + << (__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); + } + + 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); + // All subnormal numbers should round to zero. + testOneInput(func, x, 0L, false); + } + } + + void testNormalRange(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::maxNormal - FPBits::minNormal) / count; + for (UIntType i = FPBits::minNormal; i <= FPBits::maxNormal; i += step) { + F 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; + } + + long mpfrResult; + bool erangeflag = mpfr::RoundToLong(x, mpfrResult); + 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; \ + 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); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_ROUNDTOINTEGERTEST_H diff --git a/libc/test/src/math/llround_test.cpp b/libc/test/src/math/llround_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llround_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for llround ---------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/llround.h" + +LIST_ROUND_TO_INTEGER_TESTS(double, long long, __llvm_libc::llround) diff --git a/libc/test/src/math/llroundf_test.cpp b/libc/test/src/math/llroundf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llroundf_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for llroundf --------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/llroundf.h" + +LIST_ROUND_TO_INTEGER_TESTS(float, long long, __llvm_libc::llroundf) diff --git a/libc/test/src/math/llroundl_test.cpp b/libc/test/src/math/llroundl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/llroundl_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for llroundl --------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/llroundl.h" + +LIST_ROUND_TO_INTEGER_TESTS(long double, long long, __llvm_libc::llroundl) diff --git a/libc/test/src/math/lround_test.cpp b/libc/test/src/math/lround_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lround_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for lround ----------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/lround.h" + +LIST_ROUND_TO_INTEGER_TESTS(double, long, __llvm_libc::lround) diff --git a/libc/test/src/math/lroundf_test.cpp b/libc/test/src/math/lroundf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lroundf_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for lroundf ---------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/lroundf.h" + +LIST_ROUND_TO_INTEGER_TESTS(float, long, __llvm_libc::lroundf) diff --git a/libc/test/src/math/lroundl_test.cpp b/libc/test/src/math/lroundl_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/lroundl_test.cpp @@ -0,0 +1,14 @@ +//===-- Unittests for lroundl ---------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/lroundl.h" + +LIST_ROUND_TO_INTEGER_TESTS(long double, long, __llvm_libc::lroundl) diff --git a/libc/utils/FPUtil/CMakeLists.txt b/libc/utils/FPUtil/CMakeLists.txt --- a/libc/utils/FPUtil/CMakeLists.txt +++ b/libc/utils/FPUtil/CMakeLists.txt @@ -29,6 +29,9 @@ NormalFloat.h DEPENDS libc.include.math + libc.include.errno + libc.include.fenv + libc.src.errno.__errno_location libc.utils.CPP.standalone_cpp ) 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 @@ -13,6 +13,15 @@ #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 +#endif + namespace __llvm_libc { namespace fputil { @@ -150,6 +159,53 @@ } } +template ::Value && + cpp::IsIntegral::Value, + int> = 0> +static inline I roundToSignedInteger(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); + auto setDomainErrorAndRaiseInvalid = []() { +#if math_errhandling & MATH_ERRNO + llvmlibc_errno = EDOM; +#endif +#if math_errhandling & MATH_ERREXCEPT + raiseExcept(FE_INVALID); +#endif + }; + + if (bits.isInfOrNaN()) { + // Result of round is infinity or NaN only if x is infinity + // or NaN. + setDomainErrorAndRaiseInvalid(); + return bits.sign ? IntegerMin : IntegerMax; + } + + int exponent = bits.getExponent(); + constexpr int exponentLimit = sizeof(I) * 8 - 1; + if (exponent > exponentLimit) { + setDomainErrorAndRaiseInvalid(); + return bits.sign ? IntegerMin : IntegerMax; + } else if (exponent == exponentLimit) { + if (bits.sign == 0 || bits.mantissa != 0) { + setDomainErrorAndRaiseInvalid(); + return bits.sign ? IntegerMin : IntegerMax; + } + // If the control reaches here, then it means that the rounded + // 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 + // floating point value to the exact integer value. + return roundedValue; +} + } // namespace fputil } // namespace __llvm_libc 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,6 +237,8 @@ return internal::MPFRMatcher(input, t); } +template bool RoundToLong(T x, long &result); + } // namespace mpfr } // namespace testing } // namespace __llvm_libc 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 @@ -153,6 +153,16 @@ return result; } + bool roundToLong(long &result) const { + // We first calculate the rounded value. This way, when converting + // to long using mpfr_get_si, the rounding direction of MPFR_RNDN + // (or any other rounding mode), does not have an influence. + MPFRNumber roundedValue = round(); + mpfr_clear_erangeflag(); + result = mpfr_get_si(roundedValue.value, MPFR_RNDN); + return mpfr_erangeflag_p(); + } + MPFRNumber sin() const { MPFRNumber result; mpfr_sin(result.value, value, MPFR_RNDN); @@ -555,6 +565,15 @@ } // namespace internal +template bool RoundToLong(T x, long &result) { + MPFRNumber mpfr(x); + return mpfr.roundToLong(result); +} + +template bool RoundToLong(float, long &); +template bool RoundToLong(double, long &); +template bool RoundToLong(long double, long &); + } // namespace mpfr } // namespace testing } // namespace __llvm_libc