[libc] Add implementations of nextafter[f|l] functions.
A differential fuzzer for these functions has also been added. Along the way, a small correction has been done to the normal/subnormal limits of x86 long double values. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D94109
This commit is contained in:
parent
993d8ac5cb
commit
7f7b0dc4e1
@ -88,6 +88,9 @@ set(TARGET_LIBM_ENTRYPOINTS
|
||||
libc.src.math.modf
|
||||
libc.src.math.modff
|
||||
libc.src.math.modfl
|
||||
libc.src.math.nextafter
|
||||
libc.src.math.nextafterf
|
||||
libc.src.math.nextafterl
|
||||
libc.src.math.remainderf
|
||||
libc.src.math.remainder
|
||||
libc.src.math.remainderl
|
||||
|
||||
@ -144,6 +144,9 @@ set(TARGET_LIBM_ENTRYPOINTS
|
||||
libc.src.math.nearbyint
|
||||
libc.src.math.nearbyintf
|
||||
libc.src.math.nearbyintl
|
||||
libc.src.math.nextafter
|
||||
libc.src.math.nextafterf
|
||||
libc.src.math.nextafterl
|
||||
libc.src.math.remainderf
|
||||
libc.src.math.remainder
|
||||
libc.src.math.remainderl
|
||||
|
||||
@ -48,3 +48,15 @@ add_libc_fuzzer(
|
||||
libc.utils.FPUtil.fputil
|
||||
libc.utils.CPP.standalone_cpp
|
||||
)
|
||||
|
||||
add_libc_fuzzer(
|
||||
nextafter_differential_fuzz
|
||||
SRCS
|
||||
nextafter_differential_fuzz.cpp
|
||||
HDRS
|
||||
TwoInputSingleOutputDiff.h
|
||||
DEPENDS
|
||||
libc.src.math.nextafter
|
||||
libc.src.math.nextafterf
|
||||
libc.src.math.nextafterl
|
||||
)
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#define LLVM_LIBC_FUZZING_MATH_COMPARE_H
|
||||
|
||||
#include "utils/CPP/TypeTraits.h"
|
||||
#include "utils/FPUtil/FPBits.h"
|
||||
|
||||
template <typename T>
|
||||
__llvm_libc::cpp::EnableIfType<__llvm_libc::cpp::IsFloatingPointType<T>::Value,
|
||||
|
||||
26
libc/fuzzing/math/nextafter_differential_fuzz.cpp
Normal file
26
libc/fuzzing/math/nextafter_differential_fuzz.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
//===-- nextafter_differential_fuzz.cpp
|
||||
//---------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// Differential fuzz test for llvm-libc nextafter implementation.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "fuzzing/math/TwoInputSingleOutputDiff.h"
|
||||
|
||||
#include "src/math/nextafter.h"
|
||||
#include "src/math/nextafterf.h"
|
||||
#include "src/math/nextafterl.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
TwoInputSingleOutputDiff<float, float>(&__llvm_libc::nextafterf,
|
||||
&::nextafterf, data, size);
|
||||
TwoInputSingleOutputDiff<double, double>(&__llvm_libc::nextafter,
|
||||
&::nextafter, data, size);
|
||||
return 0;
|
||||
}
|
||||
@ -394,6 +394,10 @@ def StdC : StandardSpec<"stdc"> {
|
||||
FunctionSpec<"nearbyint", RetValSpec<DoubleType>, [ArgSpec<DoubleType>]>,
|
||||
FunctionSpec<"nearbyintf", RetValSpec<FloatType>, [ArgSpec<FloatType>]>,
|
||||
FunctionSpec<"nearbyintl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>]>,
|
||||
|
||||
FunctionSpec<"nextafterf", RetValSpec<FloatType>, [ArgSpec<FloatType>, ArgSpec<FloatType>]>,
|
||||
FunctionSpec<"nextafter", RetValSpec<DoubleType>, [ArgSpec<DoubleType>, ArgSpec<DoubleType>]>,
|
||||
FunctionSpec<"nextafterl", RetValSpec<LongDoubleType>, [ArgSpec<LongDoubleType>, ArgSpec<LongDoubleType>]>,
|
||||
]
|
||||
>;
|
||||
|
||||
|
||||
@ -941,3 +941,40 @@ add_entrypoint_object(
|
||||
COMPILE_OPTIONS
|
||||
-O2
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
nextafter
|
||||
SRCS
|
||||
nextafter.cpp
|
||||
HDRS
|
||||
nextafter.h
|
||||
DEPENDS
|
||||
libc.utils.FPUtil.fputil
|
||||
COMPILE_OPTIONS
|
||||
-O2
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
nextafterf
|
||||
SRCS
|
||||
nextafterf.cpp
|
||||
HDRS
|
||||
nextafterf.h
|
||||
DEPENDS
|
||||
libc.utils.FPUtil.fputil
|
||||
COMPILE_OPTIONS
|
||||
-O2
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
nextafterl
|
||||
SRCS
|
||||
nextafterl.cpp
|
||||
HDRS
|
||||
nextafterl.h
|
||||
DEPENDS
|
||||
libc.utils.FPUtil.fputil
|
||||
COMPILE_OPTIONS
|
||||
-O2
|
||||
)
|
||||
|
||||
|
||||
18
libc/src/math/nextafter.cpp
Normal file
18
libc/src/math/nextafter.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation of nextafter 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/ManipulationFunctions.h"
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
double LLVM_LIBC_ENTRYPOINT(nextafter)(double x, double y) {
|
||||
return fputil::nextafter(x, y);
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
||||
18
libc/src/math/nextafter.h
Normal file
18
libc/src/math/nextafter.h
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation header for nextafter ---------------------*- 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_NEXTAFTER_H
|
||||
#define LLVM_LIBC_SRC_MATH_NEXTAFTER_H
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
double nextafter(double x, double y);
|
||||
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#endif // LLVM_LIBC_SRC_MATH_NEXTAFTER_H
|
||||
18
libc/src/math/nextafterf.cpp
Normal file
18
libc/src/math/nextafterf.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation of nextafterf 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/ManipulationFunctions.h"
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
float LLVM_LIBC_ENTRYPOINT(nextafterf)(float x, float y) {
|
||||
return fputil::nextafter(x, y);
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
||||
18
libc/src/math/nextafterf.h
Normal file
18
libc/src/math/nextafterf.h
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation header for nextafterf --------------------*- 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_NEXTAFTERF_H
|
||||
#define LLVM_LIBC_SRC_MATH_NEXTAFTERF_H
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
float nextafterf(float x, float y);
|
||||
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#endif // LLVM_LIBC_SRC_MATH_NEXTAFTERF_H
|
||||
18
libc/src/math/nextafterl.cpp
Normal file
18
libc/src/math/nextafterl.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation of nextafterl 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/ManipulationFunctions.h"
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
long double LLVM_LIBC_ENTRYPOINT(nextafterl)(long double x, long double y) {
|
||||
return fputil::nextafter(x, y);
|
||||
}
|
||||
|
||||
} // namespace __llvm_libc
|
||||
18
libc/src/math/nextafterl.h
Normal file
18
libc/src/math/nextafterl.h
Normal file
@ -0,0 +1,18 @@
|
||||
//===-- Implementation header for nextafterl --------------------*- 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_NEXTAFTERL_H
|
||||
#define LLVM_LIBC_SRC_MATH_NEXTAFTERL_H
|
||||
|
||||
namespace __llvm_libc {
|
||||
|
||||
long double nextafterl(long double x, long double y);
|
||||
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#endif // LLVM_LIBC_SRC_MATH_NEXTAFTERL_H
|
||||
@ -1007,3 +1007,45 @@ add_fp_unittest(
|
||||
libc.src.math.hypot
|
||||
libc.utils.FPUtil.fputil
|
||||
)
|
||||
|
||||
add_fp_unittest(
|
||||
nextafter_test
|
||||
SUITE
|
||||
libc_math_unittests
|
||||
SRCS
|
||||
nextafter_test.cpp
|
||||
HDRS
|
||||
NextAfterTest.h
|
||||
DEPENDS
|
||||
libc.include.math
|
||||
libc.src.math.nextafter
|
||||
libc.utils.FPUtil.fputil
|
||||
)
|
||||
|
||||
add_fp_unittest(
|
||||
nextafterf_test
|
||||
SUITE
|
||||
libc_math_unittests
|
||||
SRCS
|
||||
nextafterf_test.cpp
|
||||
HDRS
|
||||
NextAfterTest.h
|
||||
DEPENDS
|
||||
libc.include.math
|
||||
libc.src.math.nextafterf
|
||||
libc.utils.FPUtil.fputil
|
||||
)
|
||||
|
||||
add_fp_unittest(
|
||||
nextafterl_test
|
||||
SUITE
|
||||
libc_math_unittests
|
||||
SRCS
|
||||
nextafterl_test.cpp
|
||||
HDRS
|
||||
NextAfterTest.h
|
||||
DEPENDS
|
||||
libc.include.math
|
||||
libc.src.math.nextafterl
|
||||
libc.utils.FPUtil.fputil
|
||||
)
|
||||
|
||||
193
libc/test/src/math/NextAfterTest.h
Normal file
193
libc/test/src/math/NextAfterTest.h
Normal file
@ -0,0 +1,193 @@
|
||||
//===-- Utility class to test different flavors of nextafter ----*- 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_NEXTAFTERTEST_H
|
||||
#define LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H
|
||||
|
||||
#include "utils/CPP/TypeTraits.h"
|
||||
#include "utils/FPUtil/BasicOperations.h"
|
||||
#include "utils/FPUtil/FPBits.h"
|
||||
#include "utils/FPUtil/TestHelpers.h"
|
||||
#include "utils/UnitTest/Test.h"
|
||||
#include <math.h>
|
||||
|
||||
template <typename T>
|
||||
class NextAfterTestTemplate : public __llvm_libc::testing::Test {
|
||||
using FPBits = __llvm_libc::fputil::FPBits<T>;
|
||||
using MantissaWidth = __llvm_libc::fputil::MantissaWidth<T>;
|
||||
using UIntType = typename FPBits::UIntType;
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__))
|
||||
static constexpr int bitWidthOfType =
|
||||
__llvm_libc::cpp::IsSame<T, long double>::Value ? 80 : (sizeof(T) * 8);
|
||||
#else
|
||||
static constexpr int bitWidthOfType = sizeof(T) * 8;
|
||||
#endif
|
||||
|
||||
const T zero = FPBits::zero();
|
||||
const T negZero = FPBits::negZero();
|
||||
const T inf = FPBits::inf();
|
||||
const T negInf = FPBits::negInf();
|
||||
const T nan = FPBits::buildNaN(1);
|
||||
const UIntType minSubnormal = FPBits::minSubnormal;
|
||||
const UIntType maxSubnormal = FPBits::maxSubnormal;
|
||||
const UIntType minNormal = FPBits::minNormal;
|
||||
const UIntType maxNormal = FPBits::maxNormal;
|
||||
|
||||
public:
|
||||
typedef T (*NextAfterFunc)(T, T);
|
||||
|
||||
void testNaN(NextAfterFunc func) {
|
||||
ASSERT_FP_EQ(func(nan, 0), nan);
|
||||
ASSERT_FP_EQ(func(0, nan), nan);
|
||||
}
|
||||
|
||||
void testBoundaries(NextAfterFunc func) {
|
||||
ASSERT_FP_EQ(func(zero, negZero), negZero);
|
||||
ASSERT_FP_EQ(func(negZero, zero), zero);
|
||||
|
||||
// 'from' is zero|negZero.
|
||||
T x = zero;
|
||||
T result = func(x, T(1));
|
||||
UIntType expectedBits = 1;
|
||||
T expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, T(-1));
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
x = negZero;
|
||||
result = func(x, 1);
|
||||
expectedBits = 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, -1);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
// 'from' is max subnormal value.
|
||||
x = *reinterpret_cast<const T *>(&maxSubnormal);
|
||||
result = func(x, 1);
|
||||
expected = *reinterpret_cast<const T *>(&minNormal);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, 0);
|
||||
expectedBits = maxSubnormal - 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
x = -x;
|
||||
|
||||
result = func(x, -1);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + minNormal;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, 0);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + maxSubnormal - 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
// 'from' is min subnormal value.
|
||||
x = *reinterpret_cast<const T *>(&minSubnormal);
|
||||
result = func(x, 1);
|
||||
expectedBits = minSubnormal + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
ASSERT_FP_EQ(func(x, 0), 0);
|
||||
|
||||
x = -x;
|
||||
result = func(x, -1);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + minSubnormal + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
ASSERT_FP_EQ(func(x, 0), T(-0.0));
|
||||
|
||||
// 'from' is min normal.
|
||||
x = *reinterpret_cast<const T *>(&minNormal);
|
||||
result = func(x, 0);
|
||||
expectedBits = maxSubnormal;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, inf);
|
||||
expectedBits = minNormal + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
x = -x;
|
||||
result = func(x, 0);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + maxSubnormal;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
result = func(x, -inf);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + minNormal + 1;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
|
||||
// 'from' is max normal and 'to' is infinity.
|
||||
x = *reinterpret_cast<const T *>(&maxNormal);
|
||||
result = func(x, inf);
|
||||
ASSERT_FP_EQ(result, inf);
|
||||
|
||||
result = func(-x, -inf);
|
||||
ASSERT_FP_EQ(result, -inf);
|
||||
|
||||
// 'from' is infinity.
|
||||
x = inf;
|
||||
result = func(x, 0);
|
||||
expectedBits = maxNormal;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
ASSERT_FP_EQ(func(x, inf), inf);
|
||||
|
||||
x = negInf;
|
||||
result = func(x, 0);
|
||||
expectedBits = (UIntType(1) << (bitWidthOfType - 1)) + maxNormal;
|
||||
expected = *reinterpret_cast<T *>(&expectedBits);
|
||||
ASSERT_FP_EQ(result, expected);
|
||||
ASSERT_FP_EQ(func(x, negInf), negInf);
|
||||
|
||||
// 'from' is a power of 2.
|
||||
x = T(32.0);
|
||||
result = func(x, 0);
|
||||
FPBits xBits = FPBits(x);
|
||||
FPBits resultBits = FPBits(result);
|
||||
ASSERT_EQ(resultBits.exponent, uint16_t(xBits.exponent - 1));
|
||||
ASSERT_EQ(resultBits.mantissa, (UIntType(1) << MantissaWidth::value) - 1);
|
||||
|
||||
result = func(x, T(33.0));
|
||||
resultBits = FPBits(result);
|
||||
ASSERT_EQ(resultBits.exponent, xBits.exponent);
|
||||
ASSERT_EQ(resultBits.mantissa, xBits.mantissa + UIntType(1));
|
||||
|
||||
x = -x;
|
||||
|
||||
result = func(x, 0);
|
||||
resultBits = FPBits(result);
|
||||
ASSERT_EQ(resultBits.exponent, uint16_t(xBits.exponent - 1));
|
||||
ASSERT_EQ(resultBits.mantissa, (UIntType(1) << MantissaWidth::value) - 1);
|
||||
|
||||
result = func(x, T(-33.0));
|
||||
resultBits = FPBits(result);
|
||||
ASSERT_EQ(resultBits.exponent, xBits.exponent);
|
||||
ASSERT_EQ(resultBits.mantissa, xBits.mantissa + UIntType(1));
|
||||
}
|
||||
};
|
||||
|
||||
#define LIST_NEXTAFTER_TESTS(T, func) \
|
||||
using NextAfterTest = NextAfterTestTemplate<T>; \
|
||||
TEST_F(NextAfterTest, TestNaN) { testNaN(&func); } \
|
||||
TEST_F(NextAfterTest, TestBoundaries) { testBoundaries(&func); }
|
||||
|
||||
#endif // LLVM_LIBC_TEST_SRC_MATH_NEXTAFTERTEST_H
|
||||
13
libc/test/src/math/nextafter_test.cpp
Normal file
13
libc/test/src/math/nextafter_test.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
//===-- Unittests for nextafter -------------------------------------------===//
|
||||
//
|
||||
// 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 "NextAfterTest.h"
|
||||
|
||||
#include "src/math/nextafter.h"
|
||||
|
||||
LIST_NEXTAFTER_TESTS(double, __llvm_libc::nextafter)
|
||||
13
libc/test/src/math/nextafterf_test.cpp
Normal file
13
libc/test/src/math/nextafterf_test.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
//===-- Unittests for nextafterf ------------------------------------------===//
|
||||
//
|
||||
// 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 "NextAfterTest.h"
|
||||
|
||||
#include "src/math/nextafterf.h"
|
||||
|
||||
LIST_NEXTAFTER_TESTS(float, __llvm_libc::nextafterf)
|
||||
13
libc/test/src/math/nextafterl_test.cpp
Normal file
13
libc/test/src/math/nextafterl_test.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
//===-- Unittests for nextafterl ------------------------------------------===//
|
||||
//
|
||||
// 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 "NextAfterTest.h"
|
||||
|
||||
#include "src/math/nextafterl.h"
|
||||
|
||||
LIST_NEXTAFTER_TESTS(long double, __llvm_libc::nextafterl)
|
||||
@ -36,7 +36,7 @@ template <> struct __attribute__((packed)) FPBits<long double> {
|
||||
static constexpr UIntType minSubnormal = UIntType(1);
|
||||
// Subnormal numbers include the implicit bit in x86 long double formats.
|
||||
static constexpr UIntType maxSubnormal =
|
||||
(UIntType(1) << (MantissaWidth<long double>::value + 1)) - 1;
|
||||
(UIntType(1) << (MantissaWidth<long double>::value)) - 1;
|
||||
static constexpr UIntType minNormal =
|
||||
(UIntType(3) << MantissaWidth<long double>::value);
|
||||
static constexpr UIntType maxNormal =
|
||||
|
||||
@ -143,7 +143,42 @@ static inline T ldexp(T x, int exp) {
|
||||
return normal;
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
cpp::EnableIfType<cpp::IsFloatingPointType<T>::Value, int> = 0>
|
||||
static inline T nextafter(T from, T to) {
|
||||
FPBits<T> fromBits(from);
|
||||
if (fromBits.isNaN())
|
||||
return from;
|
||||
|
||||
FPBits<T> toBits(to);
|
||||
if (toBits.isNaN())
|
||||
return to;
|
||||
|
||||
if (from == to)
|
||||
return to;
|
||||
|
||||
using UIntType = typename FPBits<T>::UIntType;
|
||||
auto intVal = fromBits.bitsAsUInt();
|
||||
UIntType signMask = (UIntType(1) << (sizeof(T) * 8 - 1));
|
||||
if (from != T(0.0)) {
|
||||
if ((from < to) == (from > T(0.0))) {
|
||||
++intVal;
|
||||
} else {
|
||||
--intVal;
|
||||
}
|
||||
} else {
|
||||
intVal = (toBits.bitsAsUInt() & signMask) + UIntType(1);
|
||||
}
|
||||
|
||||
return *reinterpret_cast<T *>(&intVal);
|
||||
// TODO: Raise floating point exceptions as required by the standard.
|
||||
}
|
||||
|
||||
} // namespace fputil
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#if (defined(__x86_64__) || defined(__i386__))
|
||||
#include "NextAfterLongDoubleX86.h"
|
||||
#endif // defined(__x86_64__) || defined(__i386__)
|
||||
|
||||
#endif // LLVM_LIBC_UTILS_FPUTIL_MANIPULATION_FUNCTIONS_H
|
||||
|
||||
114
libc/utils/FPUtil/NextAfterLongDoubleX86.h
Normal file
114
libc/utils/FPUtil/NextAfterLongDoubleX86.h
Normal file
@ -0,0 +1,114 @@
|
||||
//===-- nextafter implementation for x86 long double numbers ----*- 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_UTILS_FPUTIL_NEXT_AFTER_LONG_DOUBLE_X86_H
|
||||
#define LLVM_LIBC_UTILS_FPUTIL_NEXT_AFTER_LONG_DOUBLE_X86_H
|
||||
|
||||
#include "FPBits.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace __llvm_libc {
|
||||
namespace fputil {
|
||||
|
||||
static inline long double nextafter(long double from, long double to) {
|
||||
using FPBits = FPBits<long double>;
|
||||
FPBits fromBits(from);
|
||||
if (fromBits.isNaN())
|
||||
return from;
|
||||
|
||||
FPBits toBits(to);
|
||||
if (toBits.isNaN())
|
||||
return to;
|
||||
|
||||
if (from == to)
|
||||
return to;
|
||||
|
||||
// Convert pseudo subnormal number to normal number.
|
||||
if (fromBits.implicitBit == 1 && fromBits.exponent == 0) {
|
||||
fromBits.exponent = 1;
|
||||
}
|
||||
|
||||
using UIntType = FPBits::UIntType;
|
||||
constexpr UIntType signVal = (UIntType(1) << 79);
|
||||
constexpr UIntType mantissaMask =
|
||||
(UIntType(1) << MantissaWidth<long double>::value) - 1;
|
||||
auto intVal = fromBits.bitsAsUInt();
|
||||
if (from < 0.0l) {
|
||||
if (from > to) {
|
||||
if (intVal == (signVal + FPBits::maxSubnormal)) {
|
||||
// We deal with normal/subnormal boundary separately to avoid
|
||||
// dealing with the implicit bit.
|
||||
intVal = signVal + FPBits::minNormal;
|
||||
} else if ((intVal & mantissaMask) == mantissaMask) {
|
||||
fromBits.mantissa = 0;
|
||||
// Incrementing exponent might overflow the value to infinity,
|
||||
// which is what is expected. Since NaNs are handling separately,
|
||||
// it will never overflow "beyond" infinity.
|
||||
++fromBits.exponent;
|
||||
return fromBits;
|
||||
} else {
|
||||
++intVal;
|
||||
}
|
||||
} else {
|
||||
if (intVal == (signVal + FPBits::minNormal)) {
|
||||
// We deal with normal/subnormal boundary separately to avoid
|
||||
// dealing with the implicit bit.
|
||||
intVal = signVal + FPBits::maxSubnormal;
|
||||
} else if ((intVal & mantissaMask) == 0) {
|
||||
fromBits.mantissa = mantissaMask;
|
||||
// from == 0 is handled separately so decrementing the exponent will not
|
||||
// lead to underflow.
|
||||
--fromBits.exponent;
|
||||
return fromBits;
|
||||
} else {
|
||||
--intVal;
|
||||
}
|
||||
}
|
||||
} else if (from == 0.0l) {
|
||||
if (from > to)
|
||||
intVal = signVal + 1;
|
||||
else
|
||||
intVal = 1;
|
||||
} else {
|
||||
if (from > to) {
|
||||
if (intVal == FPBits::minNormal) {
|
||||
intVal = FPBits::maxSubnormal;
|
||||
} else if ((intVal & mantissaMask) == 0) {
|
||||
fromBits.mantissa = mantissaMask;
|
||||
// from == 0 is handled separately so decrementing the exponent will not
|
||||
// lead to underflow.
|
||||
--fromBits.exponent;
|
||||
return fromBits;
|
||||
} else {
|
||||
--intVal;
|
||||
}
|
||||
} else {
|
||||
if (intVal == FPBits::maxSubnormal) {
|
||||
intVal = FPBits::minNormal;
|
||||
} else if ((intVal & mantissaMask) == mantissaMask) {
|
||||
fromBits.mantissa = 0;
|
||||
// Incrementing exponent might overflow the value to infinity,
|
||||
// which is what is expected. Since NaNs are handling separately,
|
||||
// it will never overflow "beyond" infinity.
|
||||
++fromBits.exponent;
|
||||
return fromBits;
|
||||
} else {
|
||||
++intVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *reinterpret_cast<long double *>(&intVal);
|
||||
// TODO: Raise floating point exceptions as required by the standard.
|
||||
}
|
||||
|
||||
} // namespace fputil
|
||||
} // namespace __llvm_libc
|
||||
|
||||
#endif // LLVM_LIBC_UTILS_FPUTIL_NEXT_AFTER_LONG_DOUBLE_X86_H
|
||||
Loading…
x
Reference in New Issue
Block a user