mirror of
https://github.com/intel/llvm.git
synced 2026-01-14 11:57:39 +08:00
[libc][math] Implement nexttoward functions (#72763)
Implements the `nexttoward`, `nexttowardf` and `nexttowardl` functions. Also, raise excepts required by the standard in `nextafter` functions. cc: @lntue
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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| | | | | |
|
||||
+--------------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+
|
||||
|
||||
@@ -492,6 +492,10 @@ def StdC : StandardSpec<"stdc"> {
|
||||
FunctionSpec<"nextafter", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
|
||||
FunctionSpec<"nextafterl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
|
||||
|
||||
FunctionSpec<"nexttowardf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<LongDoubleType>]>,
|
||||
FunctionSpec<"nexttoward", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<LongDoubleType>]>,
|
||||
FunctionSpec<"nexttowardl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
|
||||
|
||||
FunctionSpec<"powf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
|
||||
FunctionSpec<"pow", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ add_header_library(
|
||||
HDRS
|
||||
ManipulationFunctions.h
|
||||
DEPENDS
|
||||
.fenv_impl
|
||||
.fp_bits
|
||||
.nearest_integer_operations
|
||||
.normal_float
|
||||
|
||||
@@ -10,12 +10,14 @@
|
||||
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_MANIPULATIONFUNCTIONS_H
|
||||
|
||||
#include "FPBits.h"
|
||||
#include "FloatProperties.h"
|
||||
#include "NearestIntegerOperations.h"
|
||||
#include "NormalFloat.h"
|
||||
#include "PlatformDefs.h"
|
||||
|
||||
#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
|
||||
|
||||
@@ -169,8 +171,50 @@ LIBC_INLINE T nextafter(T from, T to) {
|
||||
int_val = (to_bits.uintval() & sign_mask) + UIntType(1);
|
||||
}
|
||||
|
||||
UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
|
||||
if (exponent_bits == UIntType(0))
|
||||
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
|
||||
else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
|
||||
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
|
||||
|
||||
return cpp::bit_cast<T>(int_val);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T>
|
||||
nexttoward(T from, long double to) {
|
||||
FPBits<T> from_bits(from);
|
||||
if (from_bits.is_nan())
|
||||
return from;
|
||||
|
||||
FPBits<long double> to_bits(to);
|
||||
if (to_bits.is_nan())
|
||||
return to;
|
||||
|
||||
if ((long double)from == to)
|
||||
return to;
|
||||
|
||||
using UIntType = typename FPBits<T>::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<T>::MIN_SUBNORMAL;
|
||||
if (to_bits.get_sign())
|
||||
int_val |= FloatProperties<T>::SIGN_MASK;
|
||||
}
|
||||
|
||||
UIntType exponent_bits = int_val & FloatProperties<T>::EXPONENT_MASK;
|
||||
if (exponent_bits == UIntType(0))
|
||||
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
|
||||
else if (exponent_bits == FloatProperties<T>::EXPONENT_MASK)
|
||||
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
|
||||
|
||||
return cpp::bit_cast<T>(int_val);
|
||||
// TODO: Raise floating point exceptions as required by the standard.
|
||||
}
|
||||
|
||||
} // namespace fputil
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#endif
|
||||
|
||||
#include "src/__support/CPP/bit.h"
|
||||
#include "src/__support/FPUtil/FEnvImpl.h"
|
||||
#include "src/__support/FPUtil/FPBits.h"
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -59,6 +60,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 +108,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 +117,12 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
|
||||
}
|
||||
}
|
||||
|
||||
UIntType implicit_bit =
|
||||
int_val & (UIntType(1) << MantissaWidth<long double>::VALUE);
|
||||
if (implicit_bit == UIntType(0))
|
||||
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
|
||||
|
||||
return cpp::bit_cast<long double>(int_val);
|
||||
// TODO: Raise floating point exceptions as required by the standard.
|
||||
}
|
||||
|
||||
} // namespace fputil
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
-O3
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
nexttowardf
|
||||
SRCS
|
||||
nexttowardf.cpp
|
||||
HDRS
|
||||
../nexttowardf.h
|
||||
DEPENDS
|
||||
libc.src.__support.FPUtil.manipulation_functions
|
||||
COMPILE_OPTIONS
|
||||
-O3
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
nexttowardl
|
||||
SRCS
|
||||
nexttowardl.cpp
|
||||
HDRS
|
||||
../nexttowardl.h
|
||||
DEPENDS
|
||||
libc.src.__support.FPUtil.manipulation_functions
|
||||
COMPILE_OPTIONS
|
||||
-O3
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
fmod
|
||||
SRCS
|
||||
|
||||
19
libc/src/math/generic/nexttoward.cpp
Normal file
19
libc/src/math/generic/nexttoward.cpp
Normal file
@@ -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
|
||||
19
libc/src/math/generic/nexttowardf.cpp
Normal file
19
libc/src/math/generic/nexttowardf.cpp
Normal file
@@ -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
|
||||
22
libc/src/math/generic/nexttowardl.cpp
Normal file
22
libc/src/math/generic/nexttowardl.cpp
Normal file
@@ -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
|
||||
18
libc/src/math/nexttoward.h
Normal file
18
libc/src/math/nexttoward.h
Normal file
@@ -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
|
||||
18
libc/src/math/nexttowardf.h
Normal file
18
libc/src/math/nexttowardf.h
Normal file
@@ -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
|
||||
18
libc/src/math/nexttowardl.h
Normal file
18
libc/src/math/nexttowardl.h
Normal file
@@ -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
|
||||
@@ -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<float> is only
|
||||
# correctly rounded for the default rounding mode round-to-nearest tie-to-even.
|
||||
add_fp_unittest(
|
||||
|
||||
223
libc/test/src/math/smoke/NextTowardTest.h
Normal file
223
libc/test/src/math/smoke/NextTowardTest.h
Normal file
@@ -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 <fenv.h>
|
||||
#include <math.h>
|
||||
|
||||
#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 <typename T>
|
||||
class NextTowardTestTemplate : public LIBC_NAMESPACE::testing::Test {
|
||||
using FPBits = LIBC_NAMESPACE::fputil::FPBits<T>;
|
||||
using ToFPBits = LIBC_NAMESPACE::fputil::FPBits<long double>;
|
||||
using MantissaWidth = LIBC_NAMESPACE::fputil::MantissaWidth<T>;
|
||||
using UIntType = typename FPBits::UIntType;
|
||||
|
||||
static constexpr int BIT_WIDTH_OF_TYPE =
|
||||
LIBC_NAMESPACE::fputil::FloatProperties<T>::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<T>(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<T>(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<T>(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<T>(expected_bits);
|
||||
ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
|
||||
|
||||
// 'from' is max subnormal value.
|
||||
x = LIBC_NAMESPACE::cpp::bit_cast<T>(max_subnormal);
|
||||
result = func(x, 1);
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(min_normal);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, 0);
|
||||
expected_bits = max_subnormal - 1;
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(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<T>(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<T>(expected_bits);
|
||||
ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
|
||||
|
||||
// 'from' is min subnormal value.
|
||||
x = LIBC_NAMESPACE::cpp::bit_cast<T>(min_subnormal);
|
||||
result = func(x, 1);
|
||||
expected_bits = min_subnormal + 1;
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(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<T>(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<T>(min_normal);
|
||||
result = func(x, 0);
|
||||
expected_bits = max_subnormal;
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(expected_bits);
|
||||
ASSERT_FP_EQ_WITH_UNDERFLOW(result, expected);
|
||||
|
||||
result = func(x, inf);
|
||||
expected_bits = min_normal + 1;
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(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<T>(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<T>(expected_bits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
// 'from' is max normal
|
||||
x = LIBC_NAMESPACE::cpp::bit_cast<T>(max_normal);
|
||||
result = func(x, 0);
|
||||
expected_bits = max_normal - 1;
|
||||
expected = LIBC_NAMESPACE::cpp::bit_cast<T>(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<T>(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<T>(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<T>(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<T>; \
|
||||
TEST_F(LlvmLibcNextTowardTest, TestNaN) { testNaN(&func); } \
|
||||
TEST_F(LlvmLibcNextTowardTest, TestBoundaries) { testBoundaries(&func); }
|
||||
|
||||
#endif // LLVM_LIBC_TEST_SRC_MATH_NEXTTOWARDTEST_H
|
||||
13
libc/test/src/math/smoke/nexttoward_test.cpp
Normal file
13
libc/test/src/math/smoke/nexttoward_test.cpp
Normal file
@@ -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)
|
||||
13
libc/test/src/math/smoke/nexttowardf_test.cpp
Normal file
13
libc/test/src/math/smoke/nexttowardf_test.cpp
Normal file
@@ -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)
|
||||
13
libc/test/src/math/smoke/nexttowardl_test.cpp
Normal file
13
libc/test/src/math/smoke/nexttowardl_test.cpp
Normal file
@@ -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)
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user