From 6a9f4c4d716b27d65d07323796f3bcbc4333ebfe Mon Sep 17 00:00:00 2001 From: Nishant Mittal Date: Sat, 18 Nov 2023 21:41:34 +0000 Subject: [PATCH 1/2] [libc][math] Implement nexttoward functions --- libc/config/darwin/arm/entrypoints.txt | 3 + libc/config/darwin/x86_64/entrypoints.txt | 3 + libc/config/gpu/entrypoints.txt | 2 + libc/config/linux/aarch64/entrypoints.txt | 3 + libc/config/linux/riscv/entrypoints.txt | 3 + libc/config/linux/x86_64/entrypoints.txt | 3 + libc/config/windows/entrypoints.txt | 3 + libc/spec/stdc.td | 4 + .../__support/FPUtil/ManipulationFunctions.h | 45 +++- .../FPUtil/x86_64/NextAfterLongDouble.h | 10 +- libc/src/math/CMakeLists.txt | 4 + libc/src/math/generic/CMakeLists.txt | 36 +++ libc/src/math/generic/nexttoward.cpp | 19 ++ libc/src/math/generic/nexttowardf.cpp | 19 ++ libc/src/math/generic/nexttowardl.cpp | 22 ++ libc/src/math/nexttoward.h | 18 ++ libc/src/math/nexttowardf.h | 18 ++ libc/src/math/nexttowardl.h | 18 ++ libc/test/src/math/smoke/CMakeLists.txt | 48 ++++ libc/test/src/math/smoke/NextTowardTest.h | 223 ++++++++++++++++++ libc/test/src/math/smoke/nexttoward_test.cpp | 13 + libc/test/src/math/smoke/nexttowardf_test.cpp | 13 + libc/test/src/math/smoke/nexttowardl_test.cpp | 13 + 23 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 libc/src/math/generic/nexttoward.cpp create mode 100644 libc/src/math/generic/nexttowardf.cpp create mode 100644 libc/src/math/generic/nexttowardl.cpp create mode 100644 libc/src/math/nexttoward.h create mode 100644 libc/src/math/nexttowardf.h create mode 100644 libc/src/math/nexttowardl.h create mode 100644 libc/test/src/math/smoke/NextTowardTest.h create mode 100644 libc/test/src/math/smoke/nexttoward_test.cpp create mode 100644 libc/test/src/math/smoke/nexttowardf_test.cpp create mode 100644 libc/test/src/math/smoke/nexttowardl_test.cpp diff --git a/libc/config/darwin/arm/entrypoints.txt b/libc/config/darwin/arm/entrypoints.txt index 362138c92a686..da4a2345f389c 100644 --- a/libc/config/darwin/arm/entrypoints.txt +++ b/libc/config/darwin/arm/entrypoints.txt @@ -199,6 +199,9 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nextafter libc.src.math.nextafterf libc.src.math.nextafterl + libc.src.math.nexttoward + libc.src.math.nexttowardf + libc.src.math.nexttowardl libc.src.math.powf libc.src.math.remainderf libc.src.math.remainder diff --git a/libc/config/darwin/x86_64/entrypoints.txt b/libc/config/darwin/x86_64/entrypoints.txt index 57d21a2ce2735..e29e75ec92dad 100644 --- a/libc/config/darwin/x86_64/entrypoints.txt +++ b/libc/config/darwin/x86_64/entrypoints.txt @@ -178,6 +178,9 @@ set(TARGET_LIBM_ENTRYPOINTS #libc.src.math.nextafter #libc.src.math.nextafterf #libc.src.math.nextafterl + #libc.src.math.nexttoward + #libc.src.math.nexttowardf + #libc.src.math.nexttowardl #libc.src.math.remainderf #libc.src.math.remainder #libc.src.math.remainderl diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt index 5dda6aa71d36f..ba86e31ee0adc 100644 --- a/libc/config/gpu/entrypoints.txt +++ b/libc/config/gpu/entrypoints.txt @@ -219,6 +219,8 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nearbyintf libc.src.math.nextafter libc.src.math.nextafterf + libc.src.math.nexttoward + libc.src.math.nexttowardf libc.src.math.pow libc.src.math.powf libc.src.math.remainder diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index 096e2afb06e5a..284feb7b99096 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -316,6 +316,9 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nextafter libc.src.math.nextafterf libc.src.math.nextafterl + libc.src.math.nexttoward + libc.src.math.nexttowardf + libc.src.math.nexttowardl libc.src.math.powf libc.src.math.remainderf libc.src.math.remainder diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index 7dadbc7f82854..a5f0c91e32d08 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -325,6 +325,9 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nextafter libc.src.math.nextafterf libc.src.math.nextafterl + libc.src.math.nexttoward + libc.src.math.nexttowardf + libc.src.math.nexttowardl libc.src.math.powf libc.src.math.remainderf libc.src.math.remainder diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 83bc8461999ed..63aa7473115a0 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -329,6 +329,9 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nextafter libc.src.math.nextafterf libc.src.math.nextafterl + libc.src.math.nexttoward + libc.src.math.nexttowardf + libc.src.math.nexttowardl libc.src.math.powf libc.src.math.remainderf libc.src.math.remainder diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 5611b399e8bca..4c0a6ec37fe4c 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -198,6 +198,9 @@ set(TARGET_LIBM_ENTRYPOINTS libc.src.math.nextafter libc.src.math.nextafterf libc.src.math.nextafterl + libc.src.math.nexttoward + libc.src.math.nexttowardf + libc.src.math.nexttowardl libc.src.math.powf libc.src.math.remainderf libc.src.math.remainder diff --git a/libc/spec/stdc.td b/libc/spec/stdc.td index 4e38cb742d284..58c95b856535a 100644 --- a/libc/spec/stdc.td +++ b/libc/spec/stdc.td @@ -492,6 +492,10 @@ def StdC : StandardSpec<"stdc"> { FunctionSpec<"nextafter", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"nextafterl", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"nexttowardf", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"nexttoward", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"nexttowardl", RetValSpec, [ArgSpec, ArgSpec]>, + FunctionSpec<"powf", RetValSpec, [ArgSpec, ArgSpec]>, FunctionSpec<"pow", RetValSpec, [ArgSpec, ArgSpec]>, diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h index 6d62a2d0fcdf2..ff131195528dd 100644 --- a/libc/src/__support/FPUtil/ManipulationFunctions.h +++ b/libc/src/__support/FPUtil/ManipulationFunctions.h @@ -10,6 +10,7 @@ #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_MANIPULATIONFUNCTIONS_H #include "FPBits.h" +#include "FloatProperties.h" #include "NearestIntegerOperations.h" #include "NormalFloat.h" #include "PlatformDefs.h" @@ -19,6 +20,7 @@ #include "src/__support/macros/attributes.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY +#include #include #include @@ -169,8 +171,49 @@ LIBC_INLINE T nextafter(T from, T to) { int_val = (to_bits.uintval() & sign_mask) + UIntType(1); } + UIntType exponent_bits = int_val & FloatProperties::EXPONENT_MASK; + if (exponent_bits == UIntType(0)) + raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + else if (exponent_bits == FloatProperties::EXPONENT_MASK) + raise_except_if_required(FE_OVERFLOW | FE_INEXACT); + + return cpp::bit_cast(int_val); +} + +template , int> = 0> +LIBC_INLINE T nexttoward(T from, long double to) { + FPBits from_bits(from); + if (from_bits.is_nan()) + return from; + + FPBits to_bits(to); + if (to_bits.is_nan()) + return to; + + if ((long double)from == to) + return to; + + using UIntType = typename FPBits::UIntType; + UIntType int_val = from_bits.uintval(); + if (from != T(0.0)) { + if ((from < to) == (from > T(0.0))) { + ++int_val; + } else { + --int_val; + } + } else { + int_val = FPBits::MIN_SUBNORMAL; + if (to_bits.get_sign()) + int_val |= FloatProperties::SIGN_MASK; + } + + UIntType exponent_bits = int_val & FloatProperties::EXPONENT_MASK; + if (exponent_bits == UIntType(0)) + raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + else if (exponent_bits == FloatProperties::EXPONENT_MASK) + raise_except_if_required(FE_OVERFLOW | FE_INEXACT); + return cpp::bit_cast(int_val); - // TODO: Raise floating point exceptions as required by the standard. } } // namespace fputil diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h index 52ef2568c1dce..354739bab1b19 100644 --- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h +++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h @@ -59,6 +59,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) { // which is what is expected. Since NaNs are handling separately, // it will never overflow "beyond" infinity. from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1); + if (from_bits.is_inf()) + raise_except_if_required(FE_OVERFLOW | FE_INEXACT); return from_bits; } else { ++int_val; @@ -105,6 +107,8 @@ LIBC_INLINE long double nextafter(long double from, long double to) { // which is what is expected. Since NaNs are handling separately, // it will never overflow "beyond" infinity. from_bits.set_unbiased_exponent(from_bits.get_unbiased_exponent() + 1); + if (from_bits.is_inf()) + raise_except_if_required(FE_OVERFLOW | FE_INEXACT); return from_bits; } else { ++int_val; @@ -112,8 +116,12 @@ LIBC_INLINE long double nextafter(long double from, long double to) { } } + UIntType implicit_bit = + int_val & (UIntType(1) << MantissaWidth::VALUE); + if (implicit_bit == UIntType(0)) + raise_except_if_required(FE_UNDERFLOW | FE_INEXACT); + return cpp::bit_cast(int_val); - // TODO: Raise floating point exceptions as required by the standard. } } // namespace fputil diff --git a/libc/src/math/CMakeLists.txt b/libc/src/math/CMakeLists.txt index 4aea54c7a4575..ffabc27bc00ab 100644 --- a/libc/src/math/CMakeLists.txt +++ b/libc/src/math/CMakeLists.txt @@ -187,6 +187,10 @@ add_math_entrypoint_object(nextafter) add_math_entrypoint_object(nextafterf) add_math_entrypoint_object(nextafterl) +add_math_entrypoint_object(nexttoward) +add_math_entrypoint_object(nexttowardf) +add_math_entrypoint_object(nexttowardl) + add_math_entrypoint_object(pow) add_math_entrypoint_object(powf) diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index 7ac365f55bb6f..da59e74a8e822 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1474,6 +1474,42 @@ add_entrypoint_object( -O2 ) +add_entrypoint_object( + nexttoward + SRCS + nexttoward.cpp + HDRS + ../nexttoward.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + nexttowardf + SRCS + nexttowardf.cpp + HDRS + ../nexttowardf.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O2 +) + +add_entrypoint_object( + nexttowardl + SRCS + nexttowardl.cpp + HDRS + ../nexttowardl.h + DEPENDS + libc.src.__support.FPUtil.manipulation_functions + COMPILE_OPTIONS + -O2 +) + add_entrypoint_object( fmod SRCS diff --git a/libc/src/math/generic/nexttoward.cpp b/libc/src/math/generic/nexttoward.cpp new file mode 100644 index 0000000000000..38b45d6d2f65d --- /dev/null +++ b/libc/src/math/generic/nexttoward.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of nexttoward 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/math/nexttoward.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(double, nexttoward, (double x, long double y)) { + return fputil::nexttoward(x, y); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/generic/nexttowardf.cpp b/libc/src/math/generic/nexttowardf.cpp new file mode 100644 index 0000000000000..59a9f805a6946 --- /dev/null +++ b/libc/src/math/generic/nexttowardf.cpp @@ -0,0 +1,19 @@ +//===-- Implementation of nexttowardf 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/math/nexttowardf.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(float, nexttowardf, (float x, long double y)) { + return fputil::nexttoward(x, y); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/generic/nexttowardl.cpp b/libc/src/math/generic/nexttowardl.cpp new file mode 100644 index 0000000000000..0c887ae0671bc --- /dev/null +++ b/libc/src/math/generic/nexttowardl.cpp @@ -0,0 +1,22 @@ +//===-- Implementation of nexttowardl 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/math/nexttowardl.h" +#include "src/__support/FPUtil/ManipulationFunctions.h" +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE { + +LLVM_LIBC_FUNCTION(long double, nexttowardl, (long double x, long double y)) { + // We can reuse the nextafter implementation because nexttoward behaves + // exactly same as nextafter in case of long doubles. Also, we have explcitly + // handled the special 80-bit long doubles in nextafter implementation. + return fputil::nextafter(x, y); +} + +} // namespace LIBC_NAMESPACE diff --git a/libc/src/math/nexttoward.h b/libc/src/math/nexttoward.h new file mode 100644 index 0000000000000..6a5bece995768 --- /dev/null +++ b/libc/src/math/nexttoward.h @@ -0,0 +1,18 @@ +//===-- Implementation header for nexttoward --------------------*- 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_NEXTTOWARD_H +#define LLVM_LIBC_SRC_MATH_NEXTTOWARD_H + +namespace LIBC_NAMESPACE { + +double nexttoward(double x, long double y); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARD_H diff --git a/libc/src/math/nexttowardf.h b/libc/src/math/nexttowardf.h new file mode 100644 index 0000000000000..7a0eb2a61b4d4 --- /dev/null +++ b/libc/src/math/nexttowardf.h @@ -0,0 +1,18 @@ +//===-- Implementation header for nexttowardf -------------------*- 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_NEXTTOWARDF_H +#define LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H + +namespace LIBC_NAMESPACE { + +float nexttowardf(float x, long double y); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDF_H diff --git a/libc/src/math/nexttowardl.h b/libc/src/math/nexttowardl.h new file mode 100644 index 0000000000000..be1d8b298185d --- /dev/null +++ b/libc/src/math/nexttowardl.h @@ -0,0 +1,18 @@ +//===-- Implementation header for nexttowardl -------------------*- 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_NEXTTOWARDL_H +#define LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H + +namespace LIBC_NAMESPACE { + +long double nexttowardl(long double x, long double y); + +} // namespace LIBC_NAMESPACE + +#endif // LLVM_LIBC_SRC_MATH_NEXTTOWARDL_H diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt index 101bdac995355..298a1b1a400f1 100644 --- a/libc/test/src/math/smoke/CMakeLists.txt +++ b/libc/test/src/math/smoke/CMakeLists.txt @@ -1251,6 +1251,54 @@ add_fp_unittest( libc.src.__support.FPUtil.fp_bits ) +# FIXME: These tests are currently spurious for NVPTX. +if(NOT LIBC_GPU_TARGET_ARCHITECTURE_IS_NVPTX) + add_fp_unittest( + nexttoward_test + SUITE + libc-math-smoke-tests + SRCS + nexttoward_test.cpp + HDRS + NextTowardTest.h + DEPENDS + libc.include.math + libc.src.math.nexttoward + libc.src.__support.FPUtil.basic_operations + libc.src.__support.FPUtil.fp_bits + ) + + add_fp_unittest( + nexttowardf_test + SUITE + libc-math-smoke-tests + SRCS + nexttowardf_test.cpp + HDRS + NextTowardTest.h + DEPENDS + libc.include.math + libc.src.math.nexttowardf + libc.src.__support.FPUtil.basic_operations + libc.src.__support.FPUtil.fp_bits + ) +endif() + +add_fp_unittest( + nexttowardl_test + SUITE + libc-math-smoke-tests + SRCS + nexttowardl_test.cpp + HDRS + NextTowardTest.h + DEPENDS + libc.include.math + libc.src.math.nexttowardl + libc.src.__support.FPUtil.basic_operations + libc.src.__support.FPUtil.fp_bits +) + # TODO(lntue): The current implementation of fputil::general::fma is only # correctly rounded for the default rounding mode round-to-nearest tie-to-even. add_fp_unittest( diff --git a/libc/test/src/math/smoke/NextTowardTest.h b/libc/test/src/math/smoke/NextTowardTest.h new file mode 100644 index 0000000000000..4d27592a11422 --- /dev/null +++ b/libc/test/src/math/smoke/NextTowardTest.h @@ -0,0 +1,223 @@ +//===-- Utility class to test different flavors of nexttoward ---*- 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_NEXTTOWARDTEST_H +#define LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H + +#include "src/__support/CPP/bit.h" +#include "src/__support/CPP/type_traits.h" +#include "src/__support/FPUtil/BasicOperations.h" +#include "src/__support/FPUtil/FPBits.h" +#include "test/UnitTest/FPMatcher.h" +#include "test/UnitTest/Test.h" +#include +#include + +#define ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, expected_exception) \ + ASSERT_FP_EQ(result, expected); \ + ASSERT_FP_EXCEPTION(expected_exception); \ + LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT) + +#define ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected) \ + ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_UNDERFLOW) + +#define ASSERT_FP_EQ_WITH_OVERFLOW(result, expected) \ + ASSERT_FP_EQ_WITH_EXCEPTION(result, expected, FE_INEXACT | FE_OVERFLOW) + +template +class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::Test { + using FPBits = LIBC_NAMESPACE::fputil::FPBits; + using ToFPBits = LIBC_NAMESPACE::fputil::FPBits; + using MantissaWidth = LIBC_NAMESPACE::fputil::MantissaWidth; + using UIntType = typename FPBits::UIntType; + + static constexpr int BIT_WIDTH_OF_TYPE = + LIBC_NAMESPACE::fputil::FloatProperties::BIT_WIDTH; + + const T zero = T(FPBits::zero()); + const T neg_zero = T(FPBits::neg_zero()); + const T inf = T(FPBits::inf()); + const T neg_inf = T(FPBits::neg_inf()); + const T nan = T(FPBits::build_quiet_nan(1)); + + const long double to_zero = ToFPBits::zero(); + const long double to_neg_zero = ToFPBits::neg_zero(); + const long double to_nan = ToFPBits::build_quiet_nan(1); + + const UIntType min_subnormal = FPBits::MIN_SUBNORMAL; + const UIntType max_subnormal = FPBits::MAX_SUBNORMAL; + const UIntType min_normal = FPBits::MIN_NORMAL; + const UIntType max_normal = FPBits::MAX_NORMAL; + +public: + typedef T (*NextTowardFunc)(T, long double); + + void testNaN(NextTowardFunc func) { + ASSERT_FP_EQ(func(nan, 0), nan); + ASSERT_FP_EQ(func(0, to_nan), nan); + } + + void testBoundaries(NextTowardFunc func) { + ASSERT_FP_EQ(func(zero, to_neg_zero), neg_zero); + ASSERT_FP_EQ(func(neg_zero, to_zero), zero); + + // 'from' is zero|neg_zero. + T x = zero; + T result = func(x, 1); + UIntType expected_bits = 1; + T expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + result = func(x, -1); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + x = neg_zero; + result = func(x, 1); + expected_bits = 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + result = func(x, -1); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + // 'from' is max subnormal value. + x = LIBC_NAMESPACE::cpp::bit_cast(max_subnormal); + result = func(x, 1); + expected = LIBC_NAMESPACE::cpp::bit_cast(min_normal); + ASSERT_FP_EQ(result, expected); + + result = func(x, 0); + expected_bits = max_subnormal - 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + x = -x; + + result = func(x, -1); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + + result = func(x, 0); + expected_bits = + (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal - 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + // 'from' is min subnormal value. + x = LIBC_NAMESPACE::cpp::bit_cast(min_subnormal); + result = func(x, 1); + expected_bits = min_subnormal + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + ASSERT_FP_EQ_WITH_UNDERFLOW(func(x, 0), 0); + + x = -x; + result = func(x, -1); + expected_bits = + (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_subnormal + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + ASSERT_FP_EQ_WITH_UNDERFLOW(func(x, 0), T(-0.0)); + + // 'from' is min normal. + x = LIBC_NAMESPACE::cpp::bit_cast(min_normal); + result = func(x, 0); + expected_bits = max_subnormal; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + result = func(x, inf); + expected_bits = min_normal + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + + x = -x; + result = func(x, 0); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_subnormal; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected); + + result = func(x, -inf); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + min_normal + 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + + // 'from' is max normal + x = LIBC_NAMESPACE::cpp::bit_cast(max_normal); + result = func(x, 0); + expected_bits = max_normal - 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + ASSERT_FP_EQ_WITH_OVERFLOW(func(x, inf), inf); + + x = -x; + result = func(x, 0); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal - 1; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + ASSERT_FP_EQ_WITH_OVERFLOW(func(x, -inf), -inf); + + // 'from' is infinity. + x = inf; + result = func(x, 0); + expected_bits = max_normal; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + ASSERT_FP_EQ(func(x, inf), inf); + + x = neg_inf; + result = func(x, 0); + expected_bits = (UIntType(1) << (BIT_WIDTH_OF_TYPE - 1)) + max_normal; + expected = LIBC_NAMESPACE::cpp::bit_cast(expected_bits); + ASSERT_FP_EQ(result, expected); + ASSERT_FP_EQ(func(x, neg_inf), neg_inf); + + // 'from' is a power of 2. + x = T(32.0); + result = func(x, 0); + FPBits x_bits = FPBits(x); + FPBits result_bits = FPBits(result); + ASSERT_EQ(result_bits.get_unbiased_exponent(), + uint16_t(x_bits.get_unbiased_exponent() - 1)); + ASSERT_EQ(result_bits.get_mantissa(), + (UIntType(1) << MantissaWidth::VALUE) - 1); + + result = func(x, 33.0); + result_bits = FPBits(result); + ASSERT_EQ(result_bits.get_unbiased_exponent(), + x_bits.get_unbiased_exponent()); + ASSERT_EQ(result_bits.get_mantissa(), x_bits.get_mantissa() + UIntType(1)); + + x = -x; + + result = func(x, 0); + result_bits = FPBits(result); + ASSERT_EQ(result_bits.get_unbiased_exponent(), + uint16_t(x_bits.get_unbiased_exponent() - 1)); + ASSERT_EQ(result_bits.get_mantissa(), + (UIntType(1) << MantissaWidth::VALUE) - 1); + + result = func(x, -33.0); + result_bits = FPBits(result); + ASSERT_EQ(result_bits.get_unbiased_exponent(), + x_bits.get_unbiased_exponent()); + ASSERT_EQ(result_bits.get_mantissa(), x_bits.get_mantissa() + UIntType(1)); + } +}; + +#define LIST_NEXTTOWARD_TESTS(T, func) \ + using LlvmLibcNextTowardTest = NextTowardTestTemplate; \ + TEST_F(LlvmLibcNextTowardTest, TestNaN) { testNaN(&func); } \ + TEST_F(LlvmLibcNextTowardTest, TestBoundaries) { testBoundaries(&func); } + +#endif // LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H diff --git a/libc/test/src/math/smoke/nexttoward_test.cpp b/libc/test/src/math/smoke/nexttoward_test.cpp new file mode 100644 index 0000000000000..774c79efd747a --- /dev/null +++ b/libc/test/src/math/smoke/nexttoward_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nexttoward ------------------------------------------===// +// +// 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 "NextTowardTest.h" + +#include "src/math/nexttoward.h" + +LIST_NEXTTOWARD_TESTS(double, LIBC_NAMESPACE::nexttoward) diff --git a/libc/test/src/math/smoke/nexttowardf_test.cpp b/libc/test/src/math/smoke/nexttowardf_test.cpp new file mode 100644 index 0000000000000..cb6c5f9ab15f5 --- /dev/null +++ b/libc/test/src/math/smoke/nexttowardf_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nexttowardf -----------------------------------------===// +// +// 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 "NextTowardTest.h" + +#include "src/math/nexttowardf.h" + +LIST_NEXTTOWARD_TESTS(float, LIBC_NAMESPACE::nexttowardf) diff --git a/libc/test/src/math/smoke/nexttowardl_test.cpp b/libc/test/src/math/smoke/nexttowardl_test.cpp new file mode 100644 index 0000000000000..b3ee4f7887027 --- /dev/null +++ b/libc/test/src/math/smoke/nexttowardl_test.cpp @@ -0,0 +1,13 @@ +//===-- Unittests for nexttowardl -----------------------------------------===// +// +// 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 "NextTowardTest.h" + +#include "src/math/nexttowardl.h" + +LIST_NEXTTOWARD_TESTS(long double, LIBC_NAMESPACE::nexttowardl) From cde1efdd0f73eca61b5414eb6a44d5d03192fa44 Mon Sep 17 00:00:00 2001 From: Nishant Mittal Date: Tue, 21 Nov 2023 09:50:07 +0000 Subject: [PATCH 2/2] Update bazel, progress docs, address review comments --- libc/docs/math/index.rst | 6 +++--- libc/src/__support/FPUtil/CMakeLists.txt | 1 + libc/src/__support/FPUtil/ManipulationFunctions.h | 7 ++++--- libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h | 1 + libc/src/math/generic/CMakeLists.txt | 6 +++--- utils/bazel/llvm-project-overlay/libc/BUILD.bazel | 6 ++++++ 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/libc/docs/math/index.rst b/libc/docs/math/index.rst index 4af95aec60c0c..eaa7a40a29ae7 100644 --- a/libc/docs/math/index.rst +++ b/libc/docs/math/index.rst @@ -230,11 +230,11 @@ Basic Operations +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | nextafterl | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ -| nexttoward | | | | | | | | | | | | | +| nexttoward | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ -| nexttowardf | | | | | | | | | | | | | +| nexttowardf | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ -| nexttowardl | | | | | | | | | | | | | +| nexttowardl | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ | remainder | |check| | |check| | | |check| | |check| | | | |check| | | | | | +--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+ diff --git a/libc/src/__support/FPUtil/CMakeLists.txt b/libc/src/__support/FPUtil/CMakeLists.txt index 6b43a9fa4892f..164af4ac3ac54 100644 --- a/libc/src/__support/FPUtil/CMakeLists.txt +++ b/libc/src/__support/FPUtil/CMakeLists.txt @@ -94,6 +94,7 @@ add_header_library( HDRS ManipulationFunctions.h DEPENDS + .fenv_impl .fp_bits .nearest_integer_operations .normal_float diff --git a/libc/src/__support/FPUtil/ManipulationFunctions.h b/libc/src/__support/FPUtil/ManipulationFunctions.h index ff131195528dd..aa6d3220d6e12 100644 --- a/libc/src/__support/FPUtil/ManipulationFunctions.h +++ b/libc/src/__support/FPUtil/ManipulationFunctions.h @@ -17,10 +17,10 @@ #include "src/__support/CPP/bit.h" #include "src/__support/CPP/type_traits.h" +#include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/macros/attributes.h" #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY -#include #include #include @@ -180,8 +180,9 @@ LIBC_INLINE T nextafter(T from, T to) { return cpp::bit_cast(int_val); } -template , int> = 0> -LIBC_INLINE T nexttoward(T from, long double to) { +template +LIBC_INLINE cpp::enable_if_t, T> +nexttoward(T from, long double to) { FPBits from_bits(from); if (from_bits.is_nan()) return from; diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h index 354739bab1b19..4508671b47ee5 100644 --- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h +++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h @@ -16,6 +16,7 @@ #endif #include "src/__support/CPP/bit.h" +#include "src/__support/FPUtil/FEnvImpl.h" #include "src/__support/FPUtil/FPBits.h" #include diff --git a/libc/src/math/generic/CMakeLists.txt b/libc/src/math/generic/CMakeLists.txt index da59e74a8e822..413ac049a22b8 100644 --- a/libc/src/math/generic/CMakeLists.txt +++ b/libc/src/math/generic/CMakeLists.txt @@ -1483,7 +1483,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -1495,7 +1495,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( @@ -1507,7 +1507,7 @@ add_entrypoint_object( DEPENDS libc.src.__support.FPUtil.manipulation_functions COMPILE_OPTIONS - -O2 + -O3 ) add_entrypoint_object( diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel index 486146187d3a6..c89c1f7950b97 100644 --- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel @@ -1872,6 +1872,12 @@ libc_math_function(name = "nextafterf") libc_math_function(name = "nextafterl") +libc_math_function(name = "nexttoward") + +libc_math_function(name = "nexttowardf") + +libc_math_function(name = "nexttowardl") + libc_math_function(name = "scalbn") libc_math_function(name = "scalbnf")