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 @@ -55,6 +55,7 @@ libc.src.math.floor libc.src.math.floorf libc.src.math.round + libc.src.math.roundf libc.src.math.sincosf libc.src.math.sinf libc.src.math.trunc diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -9,9 +9,3 @@ DEPENDS ${TARGET_LIBM_ENTRYPOINTS} ) - -add_redirector_library( - llvmlibc_redirectors - DEPENDS - round_redirector -) 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 @@ -20,21 +20,6 @@ .math_utils ) -add_entrypoint_object( - round - REDIRECTED - SRCS - round.cpp - HDRS - round.h -) - -add_redirector_object( - round_redirector - SRC - round_redirector.cpp -) - add_entrypoint_object( cosf SRCS @@ -151,6 +136,26 @@ libc.utils.FPUtil.fputil ) +add_entrypoint_object( + round + SRCS + round.cpp + HDRS + round.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + roundf + SRCS + roundf.cpp + HDRS + roundf.h + DEPENDS + libc.utils.FPUtil.fputil +) + add_object_library( exp_utils HDRS diff --git a/libc/src/math/round.cpp b/libc/src/math/round.cpp --- a/libc/src/math/round.cpp +++ b/libc/src/math/round.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of round -------------------------------------------===// +//===-- Implementation of round function ----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,16 +6,11 @@ // //===----------------------------------------------------------------------===// -#include "src/math/round.h" - #include "src/__support/common.h" +#include "utils/FPUtil/FloatOperations.h" namespace __llvm_libc { -double __round_redirector(double x); - -double LLVM_LIBC_ENTRYPOINT(round)(double x) { - return __round_redirector(x); -} +double LLVM_LIBC_ENTRYPOINT(round)(double x) { return fputil::round(x); } } // namespace __llvm_libc diff --git a/libc/src/math/round_redirector.cpp b/libc/src/math/roundf.h rename from libc/src/math/round_redirector.cpp rename to libc/src/math/roundf.h --- a/libc/src/math/round_redirector.cpp +++ b/libc/src/math/roundf.h @@ -1,4 +1,4 @@ -//===-- Implementation of round redirector --------------------------------===// +//===-- Implementation header for roundf ------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,14 +6,13 @@ // //===----------------------------------------------------------------------===// -// Include okay for this redirector. -// NOLINTNEXTLINE(llvmlibc-restrict-system-libc-headers) -#include +#ifndef LLVM_LIBC_SRC_MATH_ROUNDF_H +#define LLVM_LIBC_SRC_MATH_ROUNDF_H namespace __llvm_libc { -double __round_redirector(double x) { - return ::round(x); -} +float roundf(float x); } // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_ROUNDF_H diff --git a/libc/src/math/round_redirector.cpp b/libc/src/math/roundf.cpp rename from libc/src/math/round_redirector.cpp rename to libc/src/math/roundf.cpp --- a/libc/src/math/round_redirector.cpp +++ b/libc/src/math/roundf.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of round redirector --------------------------------===// +//===-- Implementation of roundf function ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,14 +6,11 @@ // //===----------------------------------------------------------------------===// -// Include okay for this redirector. -// NOLINTNEXTLINE(llvmlibc-restrict-system-libc-headers) -#include +#include "src/__support/common.h" +#include "utils/FPUtil/FloatOperations.h" namespace __llvm_libc { -double __round_redirector(double x) { - return ::round(x); -} +float LLVM_LIBC_ENTRYPOINT(roundf)(float x) { return fputil::round(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 @@ -175,6 +175,32 @@ libc.utils.FPUtil.fputil ) +add_math_unittest( + round_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + round_test.cpp + DEPENDS + libc.include.math + libc.src.math.round + libc.utils.FPUtil.fputil +) + +add_math_unittest( + roundf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + roundf_test.cpp + DEPENDS + libc.include.math + libc.src.math.roundf + libc.utils.FPUtil.fputil +) + add_math_unittest( expf_test NEED_MPFR diff --git a/libc/test/src/math/round_test.cpp b/libc/test/src/math/round_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/round_test.cpp @@ -0,0 +1,84 @@ +//===-- Unittests for round -----------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/round.h" +#include "utils/FPUtil/BitPatterns.h" +#include "utils/FPUtil/FloatOperations.h" +#include "utils/FPUtil/FloatProperties.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::fputil::valueAsBits; +using __llvm_libc::fputil::valueFromBits; + +using BitPatterns = __llvm_libc::fputil::BitPatterns; +using Properties = __llvm_libc::fputil::FloatProperties; + +namespace mpfr = __llvm_libc::testing::mpfr; + +// Zero tolerance; As in, exact match with MPFR result. +static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::doublePrecision, 0, + 0}; + +TEST(RoundTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::round( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::round( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::round( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::round( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::round(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::round( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(RoundTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(1.0))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-1.0))); + EXPECT_EQ(valueAsBits(10.0), valueAsBits(__llvm_libc::round(10.0))); + EXPECT_EQ(valueAsBits(-10.0), valueAsBits(__llvm_libc::round(-10.0))); + EXPECT_EQ(valueAsBits(12345.0), valueAsBits(__llvm_libc::round(12345.0))); + EXPECT_EQ(valueAsBits(-12345.0), valueAsBits(__llvm_libc::round(-12345.0))); +} + +TEST(RoundTest, CloseToZeroNumbers) { + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(0.5))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-0.5))); + EXPECT_EQ(valueAsBits(0.0), valueAsBits(__llvm_libc::round(0.115))); + EXPECT_EQ(valueAsBits(-0.0), valueAsBits(__llvm_libc::round(-0.115))); + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::round(0.715))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::round(-0.715))); +} + +TEST(RoundTest, InDoubleRange) { + using BitsType = Properties::BitsType; + constexpr BitsType count = 1000000; + constexpr BitsType step = UINT64_MAX / count; + for (BitsType i = 0, v = 0; i <= count; ++i, v += step) { + double x = valueFromBits(v); + if (isnan(x) || isinf(x)) + continue; + ASSERT_MPFR_MATCH(mpfr::Operation::Round, x, __llvm_libc::round(x), + tolerance); + } +} diff --git a/libc/test/src/math/roundf_test.cpp b/libc/test/src/math/roundf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/roundf_test.cpp @@ -0,0 +1,85 @@ +//===-- Unittests for roundf ----------------------------------------------===// +// +// 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 "include/math.h" +#include "src/math/roundf.h" +#include "utils/FPUtil/BitPatterns.h" +#include "utils/FPUtil/FloatOperations.h" +#include "utils/FPUtil/FloatProperties.h" +#include "utils/MPFRWrapper/MPFRUtils.h" +#include "utils/UnitTest/Test.h" + +using __llvm_libc::fputil::valueAsBits; +using __llvm_libc::fputil::valueFromBits; + +using BitPatterns = __llvm_libc::fputil::BitPatterns; +using Properties = __llvm_libc::fputil::FloatProperties; + +namespace mpfr = __llvm_libc::testing::mpfr; + +// Zero tolerance; As in, exact match with MPFR result. +static constexpr mpfr::Tolerance tolerance{mpfr::Tolerance::doublePrecision, 0, + 0}; + +TEST(RoundfTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::roundf( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::roundf( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::roundf( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::roundf( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::roundf(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::roundf( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(RoundfTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(1.0f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-1.0f))); + EXPECT_EQ(valueAsBits(10.0f), valueAsBits(__llvm_libc::roundf(10.0f))); + EXPECT_EQ(valueAsBits(-10.0f), valueAsBits(__llvm_libc::roundf(-10.0f))); + EXPECT_EQ(valueAsBits(12345.0f), valueAsBits(__llvm_libc::roundf(12345.0f))); + EXPECT_EQ(valueAsBits(-12345.0f), + valueAsBits(__llvm_libc::roundf(-12345.0f))); +} + +TEST(RoundTest, CloseToZeroNumbers) { + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(0.5f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-0.5f))); + EXPECT_EQ(valueAsBits(0.0f), valueAsBits(__llvm_libc::roundf(0.115f))); + EXPECT_EQ(valueAsBits(-0.0f), valueAsBits(__llvm_libc::roundf(-0.115f))); + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::roundf(0.715f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::roundf(-0.715f))); +} + +TEST(RoundfTest, InFloatRange) { + using BitsType = Properties::BitsType; + constexpr BitsType count = 1000000; + constexpr BitsType step = UINT32_MAX / count; + for (BitsType i = 0, v = 0; i <= count; ++i, v += step) { + double x = valueFromBits(v); + if (isnan(x) || isinf(x)) + continue; + ASSERT_MPFR_MATCH(mpfr::Operation::Round, x, __llvm_libc::roundf(x), + tolerance); + } +} diff --git a/libc/utils/FPUtil/FloatOperations.h b/libc/utils/FPUtil/FloatOperations.h --- a/libc/utils/FPUtil/FloatOperations.h +++ b/libc/utils/FPUtil/FloatOperations.h @@ -214,6 +214,62 @@ } } +template ::Value, int> = 0> +static inline T round(T x) { + using Properties = FloatProperties; + using BitsType = typename FloatProperties::BitsType; + + BitsType bits = valueAsBits(x); + + // If x is infinity, NaN or zero, return it. + if (bitsAreInfOrNaN(bits) || bitsAreZero(bits)) + return x; + + bool isNeg = bits & Properties::signMask; + int exponent = getExponentFromBits(bits); + + // If the exponent is greater than the most negative mantissa + // exponent, then x is already an integer. + if (exponent >= static_cast(Properties::mantissaWidth)) + return x; + + if (exponent == -1) { + // Absolute value of x is greater than equal to 0.5 but less than 1. + if (isNeg) + return T(-1.0); + else + return T(1.0); + } + + if (exponent <= -2) { + // Absolute value of x is less than equal to 0.5. + if (isNeg) + return T(-0.0); + else + return T(0.0); + } + + uint32_t trimSize = Properties::mantissaWidth - exponent; + // If x is already an integer, return it. + if ((bits << (Properties::bitWidth - trimSize)) == 0) + return x; + + BitsType truncBits = (bits >> trimSize) << trimSize; + T truncValue = valueFromBits(truncBits); + + if ((bits & (BitsType(1) << (trimSize - 1))) == 0) { + // Franctional part is less than 0.5 so round value is the + // same as the trunc value. + return truncValue; + } + + if (isNeg) + return truncValue - T(1.0); + else + return truncValue + T(1.0); +} + } // namespace fputil } // namespace __llvm_libc