From 3f198ac94279ef8962aac96e2949b5dc036c563f Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:14:22 +0200 Subject: [PATCH 01/10] [libc][math][c23] Add ceilf16 MPFR unit tests --- libc/test/src/math/CMakeLists.txt | 18 ++++++++++++++++++ libc/test/src/math/CeilTest.h | 22 +++++++++++++--------- libc/test/src/math/ceilf16_test.cpp | 13 +++++++++++++ libc/utils/MPFRWrapper/MPFRUtils.cpp | 28 +++++++++++++++++++++++----- 4 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 libc/test/src/math/ceilf16_test.cpp diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 102188c332e40..2c839774f98fc 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -183,6 +183,7 @@ add_fp_unittest( CeilTest.h DEPENDS libc.src.math.ceil + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -197,6 +198,7 @@ add_fp_unittest( CeilTest.h DEPENDS libc.src.math.ceilf + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -211,6 +213,22 @@ add_fp_unittest( CeilTest.h DEPENDS libc.src.math.ceill + libc.src.__support.CPP.algorithm + libc.src.__support.FPUtil.fp_bits +) + +add_fp_unittest( + ceilf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + ceilf16_test.cpp + HDRS + CeilTest.h + DEPENDS + libc.src.math.ceilf16 + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) diff --git a/libc/test/src/math/CeilTest.h b/libc/test/src/math/CeilTest.h index b4c3752cc5c4b..2880b40cb80b7 100644 --- a/libc/test/src/math/CeilTest.h +++ b/libc/test/src/math/CeilTest.h @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" @@ -59,18 +60,21 @@ class CeilTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { EXPECT_FP_EQ(T(-10.0), func(T(-10.32))); EXPECT_FP_EQ(T(11.0), func(T(10.65))); EXPECT_FP_EQ(T(-10.0), func(T(-10.65))); - EXPECT_FP_EQ(T(1235.0), func(T(1234.38))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.38))); - EXPECT_FP_EQ(T(1235.0), func(T(1234.96))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.96))); + EXPECT_FP_EQ(T(124.0), func(T(123.38))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.38))); + EXPECT_FP_EQ(T(124.0), func(T(123.96))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.96))); } void testRange(CeilFunc func) { - constexpr StorageType COUNT = 100'000; - constexpr StorageType STEP = STORAGE_MAX / COUNT; - for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = FPBits(v).get_val(); - if (isnan(x) || isinf(x)) + constexpr int COUNT = 100'000; + constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max( + static_cast(STORAGE_MAX / COUNT), StorageType(1)); + StorageType v = 0; + for (int i = 0; i <= COUNT; ++i, v += STEP) { + FPBits xbits(v); + T x = xbits.get_val(); + if (xbits.is_inf_or_nan()) continue; ASSERT_MPFR_MATCH(mpfr::Operation::Ceil, x, func(x), 0.0); diff --git a/libc/test/src/math/ceilf16_test.cpp b/libc/test/src/math/ceilf16_test.cpp new file mode 100644 index 0000000000000..a6ec922836a75 --- /dev/null +++ b/libc/test/src/math/ceilf16_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for ceilf16 ---------------------------------------------===// +// +// 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 "CeilTest.h" + +#include "src/math/ceilf16.h" + +LIST_CEIL_TESTS(float16, LIBC_NAMESPACE::ceilf16) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 18a8ac044a9bb..1e7467c8abbdf 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -12,6 +12,7 @@ #include "src/__support/CPP/string_view.h" #include "src/__support/FPUtil/FPBits.h" #include "src/__support/FPUtil/fpbits_str.h" +#include "src/__support/macros/properties/types.h" #include "test/UnitTest/FPMatcher.h" #include "hdr/math_macros.h" @@ -30,6 +31,10 @@ namespace mpfr { // precision compared to the floating point precision. template struct ExtraPrecision; +template <> struct ExtraPrecision { + static constexpr unsigned int VALUE = 128; +}; + template <> struct ExtraPrecision { static constexpr unsigned int VALUE = 128; }; @@ -85,9 +90,12 @@ class MPFRNumber { // We use explicit EnableIf specializations to disallow implicit // conversions. Implicit conversions can potentially lead to loss of - // precision. - template , int> = 0> + // precision. We exceptionally allow implicit conversions from float16 + // to float, as the MPFR API does not support float16, thus requiring + // conversion to a higher-precision format. + template || + cpp::is_same_v, + int> = 0> explicit MPFRNumber(XType x, unsigned int precision = ExtraPrecision::VALUE, RoundingMode rounding = RoundingMode::Nearest) @@ -529,8 +537,9 @@ class MPFRNumber { // If the control reaches here, it means that this number and input are // of the same sign but different exponent. In such a case, ULP error is // calculated as sum of two parts. - thisAsT = std::abs(thisAsT); - input = std::abs(input); + using U = cpp::conditional_t, float, T>; + thisAsT = std::abs(static_cast(thisAsT)); + input = std::abs(static_cast(input)); T min = thisAsT > input ? input : thisAsT; T max = thisAsT > input ? thisAsT : input; int minExponent = FPBits(min).get_exponent(); @@ -585,6 +594,10 @@ template <> long double MPFRNumber::as() const { return mpfr_get_ld(value, mpfr_rounding); } +template <> float16 MPFRNumber::as() const { + return static_cast(mpfr_get_flt(value, mpfr_rounding)); +} + namespace internal { template @@ -763,6 +776,8 @@ template void explain_unary_operation_single_output_error( Operation op, double, double, double, RoundingMode); template void explain_unary_operation_single_output_error( Operation op, long double, long double, double, RoundingMode); +template void explain_unary_operation_single_output_error( + Operation op, float16, float16, double, RoundingMode); template void explain_unary_operation_two_outputs_error( @@ -942,6 +957,9 @@ template bool compare_unary_operation_single_output(Operation, double, RoundingMode); template bool compare_unary_operation_single_output( Operation, long double, long double, double, RoundingMode); +template bool compare_unary_operation_single_output(Operation, float16, + float16, double, + RoundingMode); template bool compare_unary_operation_two_outputs(Operation op, T input, From eeeb16ebbda5760ef6398abcd0087af59e373835 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:20:18 +0200 Subject: [PATCH 02/10] [libc][math][c23] Add floorf16 MPFR unit tests --- libc/test/src/math/CMakeLists.txt | 18 ++++++++++++++++++ libc/test/src/math/FloorTest.h | 22 +++++++++++++--------- libc/test/src/math/floorf16_test.cpp | 13 +++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 libc/test/src/math/floorf16_test.cpp diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 2c839774f98fc..703bfbc728890 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -243,6 +243,7 @@ add_fp_unittest( FloorTest.h DEPENDS libc.src.math.floor + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -257,6 +258,7 @@ add_fp_unittest( FloorTest.h DEPENDS libc.src.math.floorf + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -271,6 +273,22 @@ add_fp_unittest( FloorTest.h DEPENDS libc.src.math.floorl + libc.src.__support.CPP.algorithm + libc.src.__support.FPUtil.fp_bits +) + +add_fp_unittest( + floorf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + floorf16_test.cpp + HDRS + FloorTest.h + DEPENDS + libc.src.math.floorf16 + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) diff --git a/libc/test/src/math/FloorTest.h b/libc/test/src/math/FloorTest.h index 9103a5b05eb5a..cce0c731ebbc0 100644 --- a/libc/test/src/math/FloorTest.h +++ b/libc/test/src/math/FloorTest.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_TEST_SRC_MATH_FLOORTEST_H #define LLVM_LIBC_TEST_SRC_MATH_FLOORTEST_H +#include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" @@ -62,18 +63,21 @@ class FloorTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { EXPECT_FP_EQ(T(-11.0), func(T(-10.32))); EXPECT_FP_EQ(T(10.0), func(T(10.65))); EXPECT_FP_EQ(T(-11.0), func(T(-10.65))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.38))); - EXPECT_FP_EQ(T(-1235.0), func(T(-1234.38))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.96))); - EXPECT_FP_EQ(T(-1235.0), func(T(-1234.96))); + EXPECT_FP_EQ(T(123.0), func(T(123.38))); + EXPECT_FP_EQ(T(-124.0), func(T(-123.38))); + EXPECT_FP_EQ(T(123.0), func(T(123.96))); + EXPECT_FP_EQ(T(-124.0), func(T(-123.96))); } void testRange(FloorFunc func) { - constexpr StorageType COUNT = 100'000; - constexpr StorageType STEP = STORAGE_MAX / COUNT; - for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = FPBits(v).get_val(); - if (isnan(x) || isinf(x)) + constexpr int COUNT = 100'000; + constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max( + static_cast(STORAGE_MAX / COUNT), StorageType(1)); + StorageType v = 0; + for (int i = 0; i <= COUNT; ++i, v += STEP) { + FPBits xbits(v); + T x = xbits.get_val(); + if (xbits.is_inf_or_nan()) continue; ASSERT_MPFR_MATCH(mpfr::Operation::Floor, x, func(x), 0.0); diff --git a/libc/test/src/math/floorf16_test.cpp b/libc/test/src/math/floorf16_test.cpp new file mode 100644 index 0000000000000..ca5160e927035 --- /dev/null +++ b/libc/test/src/math/floorf16_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for floorf16 --------------------------------------------===// +// +// 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 "FloorTest.h" + +#include "src/math/floorf16.h" + +LIST_FLOOR_TESTS(float16, LIBC_NAMESPACE::floorf16) From 2d4aa54d5c9e792e820c11ae1d9edc99634f89e7 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:23:56 +0200 Subject: [PATCH 03/10] [libc][math][c23] Add roundf16 MPFR unit tests --- libc/test/src/math/CMakeLists.txt | 18 ++++++++++++++++++ libc/test/src/math/RoundTest.h | 22 +++++++++++++--------- libc/test/src/math/roundf16_test.cpp | 13 +++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 libc/test/src/math/roundf16_test.cpp diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index 703bfbc728890..aab5c895e01b6 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -303,6 +303,7 @@ add_fp_unittest( RoundTest.h DEPENDS libc.src.math.round + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -317,6 +318,7 @@ add_fp_unittest( RoundTest.h DEPENDS libc.src.math.roundf + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -331,6 +333,22 @@ add_fp_unittest( RoundTest.h DEPENDS libc.src.math.roundl + libc.src.__support.CPP.algorithm + libc.src.__support.FPUtil.fp_bits +) + +add_fp_unittest( + roundf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + roundf16_test.cpp + HDRS + RoundTest.h + DEPENDS + libc.src.math.roundf16 + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) diff --git a/libc/test/src/math/RoundTest.h b/libc/test/src/math/RoundTest.h index 2a31df305ac38..d571d5d8feed4 100644 --- a/libc/test/src/math/RoundTest.h +++ b/libc/test/src/math/RoundTest.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_TEST_SRC_MATH_ROUNDTEST_H #define LLVM_LIBC_TEST_SRC_MATH_ROUNDTEST_H +#include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" @@ -62,18 +63,21 @@ class RoundTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { EXPECT_FP_EQ(T(-10.0), func(T(-10.32))); EXPECT_FP_EQ(T(11.0), func(T(10.65))); EXPECT_FP_EQ(T(-11.0), func(T(-10.65))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.38))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.38))); - EXPECT_FP_EQ(T(1235.0), func(T(1234.96))); - EXPECT_FP_EQ(T(-1235.0), func(T(-1234.96))); + EXPECT_FP_EQ(T(123.0), func(T(123.38))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.38))); + EXPECT_FP_EQ(T(124.0), func(T(123.96))); + EXPECT_FP_EQ(T(-124.0), func(T(-123.96))); } void testRange(RoundFunc func) { - constexpr StorageType COUNT = 100'000; - constexpr StorageType STEP = STORAGE_MAX / COUNT; - for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = FPBits(v).get_val(); - if (isnan(x) || isinf(x)) + constexpr int COUNT = 100'000; + constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max( + static_cast(STORAGE_MAX / COUNT), StorageType(1)); + StorageType v = 0; + for (int i = 0; i <= COUNT; ++i, v += STEP) { + FPBits xbits(v); + T x = xbits.get_val(); + if (xbits.is_inf_or_nan()) continue; ASSERT_MPFR_MATCH(mpfr::Operation::Round, x, func(x), 0.0); diff --git a/libc/test/src/math/roundf16_test.cpp b/libc/test/src/math/roundf16_test.cpp new file mode 100644 index 0000000000000..54ead855934db --- /dev/null +++ b/libc/test/src/math/roundf16_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for roundf16 --------------------------------------------===// +// +// 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 "RoundTest.h" + +#include "src/math/roundf16.h" + +LIST_ROUND_TESTS(float16, LIBC_NAMESPACE::roundf16) From df553b38669e7dbbc46ead097db65a036851235d Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:28:01 +0200 Subject: [PATCH 04/10] [libc][math][c23] Add roundevenf16 MPFR unit tests --- libc/test/src/math/CMakeLists.txt | 18 ++++++++++++++ libc/test/src/math/RoundEvenTest.h | 30 ++++++++++++++---------- libc/test/src/math/roundevenf16_test.cpp | 13 ++++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) create mode 100644 libc/test/src/math/roundevenf16_test.cpp diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index aab5c895e01b6..d93a81f2173af 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -363,6 +363,7 @@ add_fp_unittest( RoundEvenTest.h DEPENDS libc.src.math.roundeven + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -377,6 +378,7 @@ add_fp_unittest( RoundEvenTest.h DEPENDS libc.src.math.roundevenf + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -391,6 +393,22 @@ add_fp_unittest( RoundEvenTest.h DEPENDS libc.src.math.roundevenl + libc.src.__support.CPP.algorithm + libc.src.__support.FPUtil.fp_bits +) + +add_fp_unittest( + roundevenf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + roundevenf16_test.cpp + HDRS + RoundEvenTest.h + DEPENDS + libc.src.math.roundevenf16 + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) diff --git a/libc/test/src/math/RoundEvenTest.h b/libc/test/src/math/RoundEvenTest.h index d70555d347659..5ecda66ccb588 100644 --- a/libc/test/src/math/RoundEvenTest.h +++ b/libc/test/src/math/RoundEvenTest.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_TEST_SRC_MATH_ROUNDEVENTEST_H #define LLVM_LIBC_TEST_SRC_MATH_ROUNDEVENTEST_H +#include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" @@ -60,22 +61,25 @@ class RoundEvenTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { EXPECT_FP_EQ(T(-2.0), func(T(-1.75))); EXPECT_FP_EQ(T(11.0), func(T(10.65))); EXPECT_FP_EQ(T(-11.0), func(T(-10.65))); - EXPECT_FP_EQ(T(1233.0), func(T(1233.25))); - EXPECT_FP_EQ(T(1234.0), func(T(1233.50))); - EXPECT_FP_EQ(T(1234.0), func(T(1233.75))); - EXPECT_FP_EQ(T(-1233.0), func(T(-1233.25))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1233.50))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1233.75))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.50))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.50))); + EXPECT_FP_EQ(T(123.0), func(T(123.25))); + EXPECT_FP_EQ(T(124.0), func(T(123.50))); + EXPECT_FP_EQ(T(124.0), func(T(123.75))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.25))); + EXPECT_FP_EQ(T(-124.0), func(T(-123.50))); + EXPECT_FP_EQ(T(-124.0), func(T(-123.75))); + EXPECT_FP_EQ(T(124.0), func(T(124.50))); + EXPECT_FP_EQ(T(-124.0), func(T(-124.50))); } void testRange(RoundEvenFunc func) { - constexpr StorageType COUNT = 100'000; - constexpr StorageType STEP = STORAGE_MAX / COUNT; - for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = FPBits(v).get_val(); - if (isnan(x) || isinf(x)) + constexpr int COUNT = 100'000; + constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max( + static_cast(STORAGE_MAX / COUNT), StorageType(1)); + StorageType v = 0; + for (int i = 0; i <= COUNT; ++i, v += STEP) { + FPBits xbits(v); + T x = xbits.get_val(); + if (xbits.is_inf_or_nan()) continue; ASSERT_MPFR_MATCH(mpfr::Operation::RoundEven, x, func(x), 0.0); diff --git a/libc/test/src/math/roundevenf16_test.cpp b/libc/test/src/math/roundevenf16_test.cpp new file mode 100644 index 0000000000000..911a32c9f73f4 --- /dev/null +++ b/libc/test/src/math/roundevenf16_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for roundevenf16 ----------------------------------------===// +// +// 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 "RoundEvenTest.h" + +#include "src/math/roundevenf16.h" + +LIST_ROUNDEVEN_TESTS(float16, LIBC_NAMESPACE::roundevenf16) From f8799a298b13a89e6a4f52eaa006086179d51349 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:32:27 +0200 Subject: [PATCH 05/10] [libc][math][c23] Fix missing header guard in CeilTest.h --- libc/test/src/math/CeilTest.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libc/test/src/math/CeilTest.h b/libc/test/src/math/CeilTest.h index 2880b40cb80b7..3af87420a739f 100644 --- a/libc/test/src/math/CeilTest.h +++ b/libc/test/src/math/CeilTest.h @@ -6,6 +6,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_TEST_SRC_MATH_CEILTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_CEILTEST_H + #include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" @@ -88,3 +91,5 @@ class CeilTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { TEST_F(LlvmLibcCeilTest, RoundedNubmers) { testRoundedNumbers(&func); } \ TEST_F(LlvmLibcCeilTest, Fractions) { testFractions(&func); } \ TEST_F(LlvmLibcCeilTest, Range) { testRange(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_CEILTEST_H From 89cf40df72b013375d4d54d353953cd097f91524 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Tue, 4 Jun 2024 19:36:18 +0200 Subject: [PATCH 06/10] [libc][math][c23] Add truncf16 MPFR unit tests --- libc/test/src/math/CMakeLists.txt | 18 ++++++++++++++++++ libc/test/src/math/TruncTest.h | 22 +++++++++++++--------- libc/test/src/math/truncf16_test.cpp | 13 +++++++++++++ 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 libc/test/src/math/truncf16_test.cpp diff --git a/libc/test/src/math/CMakeLists.txt b/libc/test/src/math/CMakeLists.txt index d93a81f2173af..e4a3087c9055f 100644 --- a/libc/test/src/math/CMakeLists.txt +++ b/libc/test/src/math/CMakeLists.txt @@ -141,6 +141,7 @@ add_fp_unittest( TruncTest.h DEPENDS libc.src.math.trunc + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -155,6 +156,7 @@ add_fp_unittest( TruncTest.h DEPENDS libc.src.math.truncf + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) @@ -169,6 +171,22 @@ add_fp_unittest( TruncTest.h DEPENDS libc.src.math.truncl + libc.src.__support.CPP.algorithm + libc.src.__support.FPUtil.fp_bits +) + +add_fp_unittest( + truncf16_test + NEED_MPFR + SUITE + libc-math-unittests + SRCS + truncf16_test.cpp + HDRS + TruncTest.h + DEPENDS + libc.src.math.truncf16 + libc.src.__support.CPP.algorithm libc.src.__support.FPUtil.fp_bits ) diff --git a/libc/test/src/math/TruncTest.h b/libc/test/src/math/TruncTest.h index bc5b76131291b..76c9740a917bf 100644 --- a/libc/test/src/math/TruncTest.h +++ b/libc/test/src/math/TruncTest.h @@ -9,6 +9,7 @@ #ifndef LLVM_LIBC_TEST_SRC_MATH_TRUNCTEST_H #define LLVM_LIBC_TEST_SRC_MATH_TRUNCTEST_H +#include "src/__support/CPP/algorithm.h" #include "test/UnitTest/FEnvSafeTest.h" #include "test/UnitTest/FPMatcher.h" #include "test/UnitTest/Test.h" @@ -62,18 +63,21 @@ class TruncTest : public LIBC_NAMESPACE::testing::FEnvSafeTest { EXPECT_FP_EQ(T(-10.0), func(T(-10.32))); EXPECT_FP_EQ(T(10.0), func(T(10.65))); EXPECT_FP_EQ(T(-10.0), func(T(-10.65))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.38))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.38))); - EXPECT_FP_EQ(T(1234.0), func(T(1234.96))); - EXPECT_FP_EQ(T(-1234.0), func(T(-1234.96))); + EXPECT_FP_EQ(T(123.0), func(T(123.38))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.38))); + EXPECT_FP_EQ(T(123.0), func(T(123.96))); + EXPECT_FP_EQ(T(-123.0), func(T(-123.96))); } void testRange(TruncFunc func) { - constexpr StorageType COUNT = 100'000; - constexpr StorageType STEP = STORAGE_MAX / COUNT; - for (StorageType i = 0, v = 0; i <= COUNT; ++i, v += STEP) { - T x = FPBits(v).get_val(); - if (isnan(x) || isinf(x)) + constexpr int COUNT = 100'000; + constexpr StorageType STEP = LIBC_NAMESPACE::cpp::max( + static_cast(STORAGE_MAX / COUNT), StorageType(1)); + StorageType v = 0; + for (int i = 0; i <= COUNT; ++i, v += STEP) { + FPBits xbits(v); + T x = xbits.get_val(); + if (xbits.is_inf_or_nan()) continue; ASSERT_MPFR_MATCH(mpfr::Operation::Trunc, x, func(x), 0.0); diff --git a/libc/test/src/math/truncf16_test.cpp b/libc/test/src/math/truncf16_test.cpp new file mode 100644 index 0000000000000..832d88ec84f8e --- /dev/null +++ b/libc/test/src/math/truncf16_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for truncf16 --------------------------------------------===// +// +// 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 "TruncTest.h" + +#include "src/math/truncf16.h" + +LIST_TRUNC_TESTS(float16, LIBC_NAMESPACE::truncf16) From 3f6df4cc0e33750217189b5d379c42b0f63b3914 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Wed, 5 Jun 2024 14:47:48 +0200 Subject: [PATCH 07/10] [libc][math][c23] Improve MPFRNumber::as() --- libc/utils/MPFRWrapper/MPFRUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 1e7467c8abbdf..4aa6ff77a4f69 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -595,7 +595,9 @@ template <> long double MPFRNumber::as() const { } template <> float16 MPFRNumber::as() const { - return static_cast(mpfr_get_flt(value, mpfr_rounding)); + // TODO: Either prove that this cast won't cause double-rounding errors, or + // find a better way to get a float16. + return static_cast(mpfr_get_d(value, mpfr_rounding)); } namespace internal { From d66fadf763143fde375e59360a0bbe4da8876904 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Wed, 5 Jun 2024 14:58:21 +0200 Subject: [PATCH 08/10] [libc][math][c23] Guard usage of float16 in MPFRUtils.cpp --- libc/utils/MPFRWrapper/MPFRUtils.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 4aa6ff77a4f69..69b6055ed522c 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -31,9 +31,11 @@ namespace mpfr { // precision compared to the floating point precision. template struct ExtraPrecision; +#ifdef LIBC_TYPES_HAS_FLOAT16 template <> struct ExtraPrecision { static constexpr unsigned int VALUE = 128; }; +#endif template <> struct ExtraPrecision { static constexpr unsigned int VALUE = 128; @@ -93,9 +95,13 @@ class MPFRNumber { // precision. We exceptionally allow implicit conversions from float16 // to float, as the MPFR API does not support float16, thus requiring // conversion to a higher-precision format. - template || - cpp::is_same_v, - int> = 0> + template +#ifdef LIBC_TYPES_HAS_FLOAT16 + || cpp::is_same_v +#endif + , + int> = 0> explicit MPFRNumber(XType x, unsigned int precision = ExtraPrecision::VALUE, RoundingMode rounding = RoundingMode::Nearest) @@ -537,7 +543,11 @@ class MPFRNumber { // If the control reaches here, it means that this number and input are // of the same sign but different exponent. In such a case, ULP error is // calculated as sum of two parts. +#ifdef LIBC_TYPES_HAS_FLOAT16 using U = cpp::conditional_t, float, T>; +#else + using U = T; +#endif thisAsT = std::abs(static_cast(thisAsT)); input = std::abs(static_cast(input)); T min = thisAsT > input ? input : thisAsT; @@ -594,11 +604,13 @@ template <> long double MPFRNumber::as() const { return mpfr_get_ld(value, mpfr_rounding); } +#ifdef LIBC_TYPES_HAS_FLOAT16 template <> float16 MPFRNumber::as() const { // TODO: Either prove that this cast won't cause double-rounding errors, or // find a better way to get a float16. return static_cast(mpfr_get_d(value, mpfr_rounding)); } +#endif namespace internal { @@ -778,8 +790,10 @@ template void explain_unary_operation_single_output_error( Operation op, double, double, double, RoundingMode); template void explain_unary_operation_single_output_error( Operation op, long double, long double, double, RoundingMode); +#ifdef LIBC_TYPES_HAS_FLOAT16 template void explain_unary_operation_single_output_error( Operation op, float16, float16, double, RoundingMode); +#endif template void explain_unary_operation_two_outputs_error( @@ -959,9 +973,11 @@ template bool compare_unary_operation_single_output(Operation, double, RoundingMode); template bool compare_unary_operation_single_output( Operation, long double, long double, double, RoundingMode); +#ifdef LIBC_TYPES_HAS_FLOAT16 template bool compare_unary_operation_single_output(Operation, float16, float16, double, RoundingMode); +#endif template bool compare_unary_operation_two_outputs(Operation op, T input, From 0014e07b17cc5682b4d8be8fffcc0409a08aedd2 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Wed, 5 Jun 2024 15:03:08 +0200 Subject: [PATCH 09/10] [libc][math][c23] Add TODO about std::abs currently not supporting float16 --- libc/utils/MPFRWrapper/MPFRUtils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 69b6055ed522c..56fcb4b989ae0 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -544,6 +544,7 @@ class MPFRNumber { // of the same sign but different exponent. In such a case, ULP error is // calculated as sum of two parts. #ifdef LIBC_TYPES_HAS_FLOAT16 + // TODO: This will no longer be needed once std::abs supports float16. using U = cpp::conditional_t, float, T>; #else using U = T; From f9ee33a4563b3852c51be0ad0dd15bc4fe3fdea5 Mon Sep 17 00:00:00 2001 From: OverMighty Date: Wed, 5 Jun 2024 17:26:41 +0200 Subject: [PATCH 10/10] [libc][math][c23] Refactor workaround for std::abs not supporting float16 --- libc/utils/MPFRWrapper/MPFRUtils.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libc/utils/MPFRWrapper/MPFRUtils.cpp b/libc/utils/MPFRWrapper/MPFRUtils.cpp index 56fcb4b989ae0..a9177fa050f86 100644 --- a/libc/utils/MPFRWrapper/MPFRUtils.cpp +++ b/libc/utils/MPFRWrapper/MPFRUtils.cpp @@ -543,14 +543,8 @@ class MPFRNumber { // If the control reaches here, it means that this number and input are // of the same sign but different exponent. In such a case, ULP error is // calculated as sum of two parts. -#ifdef LIBC_TYPES_HAS_FLOAT16 - // TODO: This will no longer be needed once std::abs supports float16. - using U = cpp::conditional_t, float, T>; -#else - using U = T; -#endif - thisAsT = std::abs(static_cast(thisAsT)); - input = std::abs(static_cast(input)); + thisAsT = FPBits(thisAsT).abs().get_val(); + input = FPBits(input).abs().get_val(); T min = thisAsT > input ? input : thisAsT; T max = thisAsT > input ? thisAsT : input; int minExponent = FPBits(min).get_exponent();