Author: Siva Chandra Reddy Date: 2020-12-11T11:12:40-08:00 New Revision: 7aeb3804c46cc6c8f291415ca09ae34021301eb8
URL: https://github.com/llvm/llvm-project/commit/7aeb3804c46cc6c8f291415ca09ae34021301eb8 DIFF: https://github.com/llvm/llvm-project/commit/7aeb3804c46cc6c8f291415ca09ae34021301eb8.diff LOG: [libc] Add implementations of lround[f|l] and llround[f|l]. A new function to MPFRWrapper has been added, which is used to set up the unit tests. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D93007 Added: libc/src/math/llround.cpp libc/src/math/llround.h libc/src/math/llroundf.cpp libc/src/math/llroundf.h libc/src/math/llroundl.cpp libc/src/math/llroundl.h libc/src/math/lround.cpp libc/src/math/lround.h libc/src/math/lroundf.cpp libc/src/math/lroundf.h libc/src/math/lroundl.cpp libc/src/math/lroundl.h libc/test/src/math/RoundToIntegerTest.h libc/test/src/math/llround_test.cpp libc/test/src/math/llroundf_test.cpp libc/test/src/math/llroundl_test.cpp libc/test/src/math/lround_test.cpp libc/test/src/math/lroundf_test.cpp libc/test/src/math/lroundl_test.cpp Modified: libc/config/linux/x86_64/entrypoints.txt libc/spec/spec.td libc/spec/stdc.td libc/src/math/CMakeLists.txt libc/test/src/math/CMakeLists.txt libc/utils/FPUtil/CMakeLists.txt libc/utils/FPUtil/NearestIntegerOperations.h libc/utils/MPFRWrapper/MPFRUtils.cpp libc/utils/MPFRWrapper/MPFRUtils.h Removed: ################################################################################ diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 4cae553ac6d4..fce28231f319 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -122,9 +122,15 @@ set(TARGET_LIBM_ENTRYPOINTS 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 index 9a31d85c148c..c78c0f053e06 100644 --- a/libc/spec/spec.td +++ b/libc/spec/spec.td @@ -39,11 +39,11 @@ class RestrictedPtrType<Type type> : Type { 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 index 051e0a89eb27..aca9a1020b1e 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -363,6 +363,14 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"roundf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>, FunctionSpec<"roundl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>, + FunctionSpec<"lround", RetValSpec<LongType>, [ArgSpec<DoubleType>]>, + FunctionSpec<"lroundf", RetValSpec<LongType>, [ArgSpec<FloatType>]>, + FunctionSpec<"lroundl", RetValSpec<LongType>, [ArgSpec<LongDoubleType>]>, + + FunctionSpec<"llround", RetValSpec<LongLongType>, [ArgSpec<DoubleType>]>, + FunctionSpec<"llroundf", RetValSpec<LongLongType>, [ArgSpec<FloatType>]>, + FunctionSpec<"llroundl", RetValSpec<LongLongType>, [ArgSpec<LongDoubleType>]>, + FunctionSpec<"sqrt", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>, FunctionSpec<"sqrtf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>, FunctionSpec<"sqrtl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>, diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 8201d737ddb1..c4de1b1eae86 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -236,6 +236,78 @@ add_entrypoint_object( -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.cpp b/libc/src/math/llround.cpp new file mode 100644 index 000000000000..25f7e91cf129 --- /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<double, long long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llround.h b/libc/src/math/llround.h new file mode 100644 index 000000000000..5b2b7fc96df8 --- /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/llroundf.cpp b/libc/src/math/llroundf.cpp new file mode 100644 index 000000000000..e0b34710594a --- /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<float, long long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llroundf.h b/libc/src/math/llroundf.h new file mode 100644 index 000000000000..65faad1e07a6 --- /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/llroundl.cpp b/libc/src/math/llroundl.cpp new file mode 100644 index 000000000000..4df0b8138bf7 --- /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<long double, long long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/llroundl.h b/libc/src/math/llroundl.h new file mode 100644 index 000000000000..f859485a730b --- /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/lround.cpp b/libc/src/math/lround.cpp new file mode 100644 index 000000000000..3fb9028f84fa --- /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<double, long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lround.h b/libc/src/math/lround.h new file mode 100644 index 000000000000..8c555e816c8b --- /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/lroundf.cpp b/libc/src/math/lroundf.cpp new file mode 100644 index 000000000000..0388bdffcd69 --- /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<float, long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lroundf.h b/libc/src/math/lroundf.h new file mode 100644 index 000000000000..3d318a7a2fab --- /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/lroundl.cpp b/libc/src/math/lroundl.cpp new file mode 100644 index 000000000000..27310ae68d68 --- /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<long double, long>(x); +} + +} // namespace __llvm_libc diff --git a/libc/src/math/lroundl.h b/libc/src/math/lroundl.h new file mode 100644 index 000000000000..a8b5aff0fcf3 --- /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/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 8635e7aba427..44abc401da44 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -267,6 +267,126 @@ add_fp_unittest( 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 index 000000000000..7c0605ebcadd --- /dev/null +++ b/libc/test/src/math/RoundToIntegerTest.h @@ -0,0 +1,217 @@ +//===-- Utility class to test diff erent 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 <math.h> +#if math_errhandling & MATH_ERRNO +#include <errno.h> +#endif +#if math_errhandling & MATH_ERREXCEPT +#include "utils/FPUtil/FEnv.h" +#endif + +namespace mpfr = __llvm_libc::testing::mpfr; + +template <typename F, typename I> +class RoundToIntegerTestTemplate : public __llvm_libc::testing::Test { +public: + typedef I (*RoundToIntegerFunc)(F); + +private: + using FPBits = __llvm_libc::fputil::FPBits<F>; + using UIntType = typename FPBits::UIntType; + + const F zero = __llvm_libc::fputil::FPBits<F>::zero(); + const F negZero = __llvm_libc::fputil::FPBits<F>::negZero(); + const F inf = __llvm_libc::fputil::FPBits<F>::inf(); + const F negInf = __llvm_libc::fputil::FPBits<F>::negInf(); + const F nan = __llvm_libc::fputil::FPBits<F>::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<F>::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<F, I>; \ + 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 index 000000000000..e7d5b9006621 --- /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 index 000000000000..35644a7a6829 --- /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 index 000000000000..9f19c3516c32 --- /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 index 000000000000..96634f84c1a8 --- /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 index 000000000000..9df427386cb0 --- /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 index 000000000000..27429a83ee7b --- /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 index a2d2aa18a953..c54184ec775f 100644 --- a/libc/utils/FPUtil/CMakeLists.txt +++ b/libc/utils/FPUtil/CMakeLists.txt @@ -29,6 +29,9 @@ add_header_library( 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 index 1c8ad36274c8..98a2f6f6b139 100644 --- a/libc/utils/FPUtil/NearestIntegerOperations.h +++ b/libc/utils/FPUtil/NearestIntegerOperations.h @@ -13,6 +13,15 @@ #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> +#endif + namespace __llvm_libc { namespace fputil { @@ -150,6 +159,53 @@ static inline T round(T x) { } } +template <typename F, typename I, + cpp::EnableIfType<cpp::IsFloatingPointType<F>::Value && + cpp::IsIntegral<I>::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>; + 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.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 56764e9740b0..6f3d216b72c9 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -153,6 +153,16 @@ class MPFRNumber { 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 @@ template bool compareBinaryOperationOneOutput<long double>( } // namespace internal +template <typename T> bool RoundToLong(T x, long &result) { + MPFRNumber mpfr(x); + return mpfr.roundToLong(result); +} + +template bool RoundToLong<float>(float, long &); +template bool RoundToLong<double>(double, long &); +template bool RoundToLong<long double>(long double, long &); + } // namespace mpfr } // namespace testing } // namespace __llvm_libc diff --git a/libc/utils/MPFRWrapper/MPFRUtils.h b/libc/utils/MPFRWrapper/MPFRUtils.h index 6fb9fe5c47b6..cc0dab66947e 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.h +++ b/libc/utils/MPFRWrapper/MPFRUtils.h @@ -237,6 +237,8 @@ getMPFRMatcher(InputType input, OutputType outputUnused, double t) { return internal::MPFRMatcher<op, InputType, OutputType>(input, t); } +template <typename T> bool RoundToLong(T x, long &result); + } // namespace mpfr } // namespace testing } // namespace __llvm_libc _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits