193 changes: 193 additions & 0 deletions libc/test/src/math/NextAfterTest.h
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions libc/test/src/math/nextafter_test.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions libc/test/src/math/nextafterf_test.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions libc/test/src/math/nextafterl_test.cpp
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 1 addition & 1 deletion libc/utils/FPUtil/LongDoubleBitsX86.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
35 changes: 35 additions & 0 deletions libc/utils/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 114 additions & 0 deletions libc/utils/FPUtil/NextAfterLongDoubleX86.h
Original file line number Diff line number Diff line change
@@ -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