diff --git a/libc/lib/CMakeLists.txt b/libc/lib/CMakeLists.txt --- a/libc/lib/CMakeLists.txt +++ b/libc/lib/CMakeLists.txt @@ -49,14 +49,20 @@ llvmlibm DEPENDS # math.h entrypoints + libc.src.math.ceil + libc.src.math.ceilf libc.src.math.cosf libc.src.math.fabs libc.src.math.fabsf + libc.src.math.floor + libc.src.math.floorf libc.src.math.expf libc.src.math.exp2f libc.src.math.round libc.src.math.sincosf libc.src.math.sinf + libc.src.math.trunc + libc.src.math.truncf ) add_redirector_library( 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 @@ -91,6 +91,66 @@ libc.utils.FPUtil.fputil ) +add_entrypoint_object( + trunc + SRCS + trunc.cpp + HDRS + trunc.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + truncf + SRCS + truncf.cpp + HDRS + truncf.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + ceil + SRCS + ceil.cpp + HDRS + ceil.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + ceilf + SRCS + ceilf.cpp + HDRS + ceilf.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + floor + SRCS + floor.cpp + HDRS + floor.h + DEPENDS + libc.utils.FPUtil.fputil +) + +add_entrypoint_object( + floorf + SRCS + floorf.cpp + HDRS + floorf.h + DEPENDS + libc.utils.FPUtil.fputil +) + add_object_library( exp_utils HDRS diff --git a/libc/src/math/ceil.h b/libc/src/math/ceil.h new file mode 100644 --- /dev/null +++ b/libc/src/math/ceil.h @@ -0,0 +1,18 @@ +//===-- Implementation header for ceil --------------------------*- 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_CEIL_H +#define LLVM_LIBC_SRC_MATH_CEIL_H + +namespace __llvm_libc { + +double ceil(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_CEIL_H diff --git a/libc/src/math/ceil.cpp b/libc/src/math/ceil.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/ceil.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of ceil 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/FloatOperations.h" + +namespace __llvm_libc { + +double LLVM_LIBC_ENTRYPOINT(ceil)(double x) { return fputil::ceil(x); } + +} // namespace __llvm_libc diff --git a/libc/src/math/ceilf.h b/libc/src/math/ceilf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/ceilf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for ceilf -------------------------*- 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_CEILF_H +#define LLVM_LIBC_SRC_MATH_CEILF_H + +namespace __llvm_libc { + +float ceilf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_CEILF_H diff --git a/libc/src/math/ceilf.cpp b/libc/src/math/ceilf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/ceilf.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of ceilf 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/FloatOperations.h" + +namespace __llvm_libc { + +float LLVM_LIBC_ENTRYPOINT(ceilf)(float x) { return fputil::ceil(x); } + +} // namespace __llvm_libc diff --git a/libc/src/math/floor.h b/libc/src/math/floor.h new file mode 100644 --- /dev/null +++ b/libc/src/math/floor.h @@ -0,0 +1,18 @@ +//===-- Implementation header for floor -------------------------*- 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_FLOOR_H +#define LLVM_LIBC_SRC_MATH_FLOOR_H + +namespace __llvm_libc { + +double floor(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FLOOR_H diff --git a/libc/src/math/floor.cpp b/libc/src/math/floor.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/floor.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of floor 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/FloatOperations.h" + +namespace __llvm_libc { + +double LLVM_LIBC_ENTRYPOINT(floor)(double x) { return fputil::floor(x); } + +} // namespace __llvm_libc diff --git a/libc/src/math/floorf.h b/libc/src/math/floorf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/floorf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for floorf ------------------------*- 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_FLOORF_H +#define LLVM_LIBC_SRC_MATH_FLOORF_H + +namespace __llvm_libc { + +float floorf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_FLOORF_H diff --git a/libc/src/math/floorf.cpp b/libc/src/math/floorf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/floorf.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of floorf 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/FloatOperations.h" + +namespace __llvm_libc { + +float LLVM_LIBC_ENTRYPOINT(floorf)(float x) { return fputil::floor(x); } + +} // namespace __llvm_libc diff --git a/libc/src/math/trunc.h b/libc/src/math/trunc.h new file mode 100644 --- /dev/null +++ b/libc/src/math/trunc.h @@ -0,0 +1,18 @@ +//===-- Implementation header for trunc -------------------------*- 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_TRUNC_H +#define LLVM_LIBC_SRC_MATH_TRUNC_H + +namespace __llvm_libc { + +double trunc(double x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_TRUNC_H diff --git a/libc/src/math/trunc.cpp b/libc/src/math/trunc.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/trunc.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of trunc 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/FloatOperations.h" + +namespace __llvm_libc { + +double LLVM_LIBC_ENTRYPOINT(trunc)(double x) { return fputil::trunc(x); } + +} // namespace __llvm_libc diff --git a/libc/src/math/truncf.h b/libc/src/math/truncf.h new file mode 100644 --- /dev/null +++ b/libc/src/math/truncf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for truncf ------------------------*- 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_TRUNCF_H +#define LLVM_LIBC_SRC_MATH_TRUNCF_H + +namespace __llvm_libc { + +float truncf(float x); + +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_MATH_TRUNCF_H diff --git a/libc/src/math/truncf.cpp b/libc/src/math/truncf.cpp new file mode 100644 --- /dev/null +++ b/libc/src/math/truncf.cpp @@ -0,0 +1,16 @@ +//===-- Implementation of truncf 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/FloatOperations.h" + +namespace __llvm_libc { + +float LLVM_LIBC_ENTRYPOINT(truncf)(float x) { return fputil::trunc(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 @@ -97,6 +97,84 @@ libc.utils.FPUtil.fputil ) +add_math_unittest( + trunc_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + trunc_test.cpp + DEPENDS + libc.include.math + libc.src.math.trunc + libc.utils.FPUtil.fputil +) + +add_math_unittest( + truncf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + truncf_test.cpp + DEPENDS + libc.include.math + libc.src.math.truncf + libc.utils.FPUtil.fputil +) + +add_math_unittest( + ceil_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + ceil_test.cpp + DEPENDS + libc.include.math + libc.src.math.ceil + libc.utils.FPUtil.fputil +) + +add_math_unittest( + ceilf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + ceilf_test.cpp + DEPENDS + libc.include.math + libc.src.math.ceilf + libc.utils.FPUtil.fputil +) + +add_math_unittest( + floor_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + floor_test.cpp + DEPENDS + libc.include.math + libc.src.math.floor + libc.utils.FPUtil.fputil +) + +add_math_unittest( + floorf_test + NEED_MPFR + SUITE + libc_math_unittests + SRCS + floorf_test.cpp + DEPENDS + libc.include.math + libc.src.math.floorf + libc.utils.FPUtil.fputil +) + add_math_unittest( expf_test NEED_MPFR diff --git a/libc/test/src/math/ceil_test.cpp b/libc/test/src/math/ceil_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/ceil_test.cpp @@ -0,0 +1,75 @@ +//===-- Unittests for ceil ------------------------------------------------===// +// +// 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/ceil.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(ceilTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::ceil(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::ceil( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits( + __llvm_libc::ceil(valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::ceil( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::ceil(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, + valueAsBits(__llvm_libc::ceil(valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::ceil(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::ceil( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(ceilTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::ceil(1.0))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::ceil(-1.0))); + EXPECT_EQ(valueAsBits(10.0), valueAsBits(__llvm_libc::ceil(10.0))); + EXPECT_EQ(valueAsBits(-10.0), valueAsBits(__llvm_libc::ceil(-10.0))); + EXPECT_EQ(valueAsBits(12345.0), valueAsBits(__llvm_libc::ceil(12345.0))); + EXPECT_EQ(valueAsBits(-12345.0), valueAsBits(__llvm_libc::ceil(-12345.0))); +} + +TEST(ceilTest, 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::Ceil, x, __llvm_libc::ceil(x), + tolerance); + } +} diff --git a/libc/test/src/math/ceilf_test.cpp b/libc/test/src/math/ceilf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/ceilf_test.cpp @@ -0,0 +1,75 @@ +//===-- Unittests for ceilf -----------------------------------------------===// +// +// 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/ceilf.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(CeilfTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::ceilf(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::ceilf( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::ceilf( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::ceilf( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::ceilf(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::ceilf( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::ceilf(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::ceilf( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(ceilfTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::ceilf(1.0f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::ceilf(-1.0f))); + EXPECT_EQ(valueAsBits(10.0f), valueAsBits(__llvm_libc::ceilf(10.0f))); + EXPECT_EQ(valueAsBits(-10.0f), valueAsBits(__llvm_libc::ceilf(-10.0f))); + EXPECT_EQ(valueAsBits(12345.0f), valueAsBits(__llvm_libc::ceilf(12345.0f))); + EXPECT_EQ(valueAsBits(-12345.0f), valueAsBits(__llvm_libc::ceilf(-12345.0f))); +} + +TEST(ceilfTest, 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::Ceil, x, __llvm_libc::ceilf(x), + tolerance); + } +} diff --git a/libc/test/src/math/floor_test.cpp b/libc/test/src/math/floor_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/floor_test.cpp @@ -0,0 +1,75 @@ +//===-- Unittests for floor -----------------------------------------------===// +// +// 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/floor.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(FloorTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::floor(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::floor( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::floor( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::floor( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::floor(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::floor( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::floor(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::floor( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(floorTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::floor(1.0))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::floor(-1.0))); + EXPECT_EQ(valueAsBits(10.0), valueAsBits(__llvm_libc::floor(10.0))); + EXPECT_EQ(valueAsBits(-10.0), valueAsBits(__llvm_libc::floor(-10.0))); + EXPECT_EQ(valueAsBits(12345.0), valueAsBits(__llvm_libc::floor(12345.0))); + EXPECT_EQ(valueAsBits(-12345.0), valueAsBits(__llvm_libc::floor(-12345.0))); +} + +TEST(floorTest, 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::Floor, x, __llvm_libc::floor(x), + tolerance); + } +} diff --git a/libc/test/src/math/floorf_test.cpp b/libc/test/src/math/floorf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/floorf_test.cpp @@ -0,0 +1,76 @@ +//===-- Unittests for floorf ----------------------------------------------===// +// +// 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/floorf.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(FloorfTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::floorf(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::floorf( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::floorf( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::floorf( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::floorf(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::floorf( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::floorf(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::floorf( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(floorfTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::floorf(1.0f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::floorf(-1.0f))); + EXPECT_EQ(valueAsBits(10.0f), valueAsBits(__llvm_libc::floorf(10.0f))); + EXPECT_EQ(valueAsBits(-10.0f), valueAsBits(__llvm_libc::floorf(-10.0f))); + EXPECT_EQ(valueAsBits(12345.0f), valueAsBits(__llvm_libc::floorf(12345.0f))); + EXPECT_EQ(valueAsBits(-12345.0f), + valueAsBits(__llvm_libc::floorf(-12345.0f))); +} + +TEST(floorfTest, 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::Floor, x, __llvm_libc::floorf(x), + tolerance); + } +} diff --git a/libc/test/src/math/trunc_test.cpp b/libc/test/src/math/trunc_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/trunc_test.cpp @@ -0,0 +1,75 @@ +//===-- Unittests for trunc -----------------------------------------------===// +// +// 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/trunc.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(TruncTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::trunc(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::trunc( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::trunc( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::trunc( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::trunc(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::trunc( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::trunc(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::trunc( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(TruncTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0), valueAsBits(__llvm_libc::trunc(1.0))); + EXPECT_EQ(valueAsBits(-1.0), valueAsBits(__llvm_libc::trunc(-1.0))); + EXPECT_EQ(valueAsBits(10.0), valueAsBits(__llvm_libc::trunc(10.0))); + EXPECT_EQ(valueAsBits(-10.0), valueAsBits(__llvm_libc::trunc(-10.0))); + EXPECT_EQ(valueAsBits(12345.0), valueAsBits(__llvm_libc::trunc(12345.0))); + EXPECT_EQ(valueAsBits(-12345.0), valueAsBits(__llvm_libc::trunc(-12345.0))); +} + +TEST(truncTest, 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::Trunc, x, __llvm_libc::trunc(x), + tolerance); + } +} diff --git a/libc/test/src/math/truncf_test.cpp b/libc/test/src/math/truncf_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/math/truncf_test.cpp @@ -0,0 +1,77 @@ +//===-- Unittests for truncf ----------------------------------------------===// +// +// 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/truncf.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::floatPrecision, 0, + 0}; + +namespace mpfr = __llvm_libc::testing::mpfr; +TEST(TruncfTest, SpecialNumbers) { + EXPECT_EQ( + BitPatterns::aQuietNaN, + valueAsBits(__llvm_libc::truncf(valueFromBits(BitPatterns::aQuietNaN)))); + EXPECT_EQ(BitPatterns::aNegativeQuietNaN, + valueAsBits(__llvm_libc::truncf( + valueFromBits(BitPatterns::aNegativeQuietNaN)))); + + EXPECT_EQ(BitPatterns::aSignallingNaN, + valueAsBits(__llvm_libc::truncf( + valueFromBits(BitPatterns::aSignallingNaN)))); + EXPECT_EQ(BitPatterns::aNegativeSignallingNaN, + valueAsBits(__llvm_libc::truncf( + valueFromBits(BitPatterns::aNegativeSignallingNaN)))); + + EXPECT_EQ(BitPatterns::inf, + valueAsBits(__llvm_libc::truncf(valueFromBits(BitPatterns::inf)))); + EXPECT_EQ(BitPatterns::negInf, valueAsBits(__llvm_libc::truncf( + valueFromBits(BitPatterns::negInf)))); + + EXPECT_EQ(BitPatterns::zero, + valueAsBits(__llvm_libc::truncf(valueFromBits(BitPatterns::zero)))); + EXPECT_EQ(BitPatterns::negZero, valueAsBits(__llvm_libc::truncf( + valueFromBits(BitPatterns::negZero)))); +} + +TEST(TruncTest, RoundedNumbers) { + EXPECT_EQ(valueAsBits(1.0f), valueAsBits(__llvm_libc::truncf(1.0f))); + EXPECT_EQ(valueAsBits(-1.0f), valueAsBits(__llvm_libc::truncf(-1.0f))); + EXPECT_EQ(valueAsBits(10.0f), valueAsBits(__llvm_libc::truncf(10.0f))); + EXPECT_EQ(valueAsBits(-10.0f), valueAsBits(__llvm_libc::truncf(-10.0f))); + EXPECT_EQ(valueAsBits(12345.0f), valueAsBits(__llvm_libc::truncf(12345.0f))); + EXPECT_EQ(valueAsBits(-12345.0f), + valueAsBits(__llvm_libc::truncf(-12345.0f))); +} + +TEST(truncfTest, 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::Trunc, x, __llvm_libc::truncf(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 @@ -40,18 +40,23 @@ return valueAsBits(x) & (~FloatProperties::signMask); } -// Return the zero adjusted exponent value of x. -template ::Value, int> = 0> -int getExponent(T x) { - using Properties = FloatProperties; - using BitsType = typename Properties::BitsType; - BitsType bits = absBits(x); +template +static inline int getExponentFromBits(BitsType bits) { + using FPType = typename FloatType::Type; + using Properties = FloatProperties; + bits = bits & Properties::exponentMask; int e = (bits >> Properties::mantissaWidth); // Shift out the mantissa. e -= Properties::exponentOffset; // Zero adjust. return e; } +// Return the zero adjusted exponent value of x. +template ::Value, int> = 0> +static inline int getExponent(T x) { + return getExponentFromBits(valueAsBits(x)); +} + // Return true if x is infinity (positive or negative.) template ::Value, int> = 0> @@ -74,6 +79,24 @@ ((bits & Properties::mantissaMask) != 0); } +template static inline bool bitsAreInfOrNaN(BitsType bits) { + using FPType = typename FloatType::Type; + return (bits & BitPatterns::inf) == BitPatterns::inf; +} + +template static inline bool bitsAreZero(BitsType bits) { + using FPType = typename FloatType::Type; + return (bits == BitPatterns::zero) || + (bits == BitPatterns::negZero); +} + +// Return true if x is any kind of NaN or infinity. +template ::Value, int> = 0> +static inline bool isInfOrNaN(T x) { + return bitsAreInfOrNaN(valueAsBits(x)); +} + // Return true if x is a quiet NAN. template ::Value, int> = 0> @@ -103,6 +126,94 @@ return valueFromBits(absBits(x)); } +// Return the trucated value of x. If x is non-negative, then the return value +// is greatest integer less than or equal to x. Otherwise, return the smallest +// integer greater than or equal to x. That is, return the integer value rounded +// toward zero. +template ::Value, int> = 0> +static inline T trunc(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; + + 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 the exponent is such that abs(x) is less than 1, then return 0. + if (exponent <= -1) { + if (Properties::signMask & bits) + return T(-0.0); + else + return T(0.0); + } + + uint32_t trimSize = Properties::mantissaWidth - exponent; + return valueFromBits((bits >> trimSize) << trimSize); +} + +template ::Value, int> = 0> +static inline T ceil(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) { + if (isNeg) + return T(-0.0); + else + return T(1.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 x is negative, the ceil operation is equivalent to the trunc operation. + if (isNeg) + return truncValue; + + return truncValue + T(1.0); +} + +template ::Value, int> = 0> +static inline T floor(T x) { + auto bits = valueAsBits(x); + if (FloatProperties::signMask & bits) { + return -ceil(-x); + } else { + return trunc(x); + } +} + } // namespace fputil } // namespace __llvm_libc diff --git a/libc/utils/FPUtil/FloatProperties.h b/libc/utils/FPUtil/FloatProperties.h --- a/libc/utils/FPUtil/FloatProperties.h +++ b/libc/utils/FPUtil/FloatProperties.h @@ -21,9 +21,12 @@ static_assert(sizeof(BitsType) == sizeof(float), "Unexpected size of 'float' type."); + static constexpr uint32_t bitWidth = sizeof(BitsType) << 3; + static constexpr uint32_t mantissaWidth = 23; static constexpr BitsType mantissaMask = 0x007fffffU; static constexpr BitsType signMask = 0x80000000U; + static constexpr BitsType exponentMask = ~(signMask | mantissaMask); static constexpr uint32_t exponentOffset = 127; // If a number x is a NAN, then it is a quiet NAN if: @@ -37,9 +40,12 @@ static_assert(sizeof(BitsType) == sizeof(double), "Unexpected size of 'double' type."); + static constexpr uint32_t bitWidth = sizeof(BitsType) << 3; + static constexpr uint32_t mantissaWidth = 52; static constexpr BitsType mantissaMask = 0x000fffffffffffffU; static constexpr BitsType signMask = 0x8000000000000000ULL; + static constexpr BitsType exponentMask = ~(signMask | mantissaMask); static constexpr uint32_t exponentOffset = 1023; // If a number x is a NAN, then it is a quiet NAN if: 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 @@ -39,7 +39,17 @@ uint32_t bits; }; -enum class Operation : int { Abs, Cos, Exp, Exp2, Sin }; +enum class Operation : int { + Abs, + Ceil, + Cos, + Exp, + Exp2, + Floor, + Round, + Sin, + Trunc +}; namespace internal { 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 @@ -89,6 +89,9 @@ case Operation::Abs: mpfr_abs(value, mpfrInput.value, MPFR_RNDN); break; + case Operation::Ceil: + mpfr_ceil(value, mpfrInput.value); + break; case Operation::Cos: mpfr_cos(value, mpfrInput.value, MPFR_RNDN); break; @@ -98,9 +101,18 @@ case Operation::Exp2: mpfr_exp2(value, mpfrInput.value, MPFR_RNDN); break; + case Operation::Floor: + mpfr_floor(value, mpfrInput.value); + break; + case Operation::Round: + mpfr_round(value, mpfrInput.value); + break; case Operation::Sin: mpfr_sin(value, mpfrInput.value, MPFR_RNDN); break; + case Operation::Trunc: + mpfr_trunc(value, mpfrInput.value); + break; } }