Skip to content

[libc][NFC] unify nextafter and nexttoward code #73698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 11 additions & 44 deletions libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,59 +144,26 @@ LIBC_INLINE T ldexp(T x, int exp) {
return normal;
}

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T nextafter(T from, T to) {
FPBits<T> from_bits(from);
if (from_bits.is_nan())
return from;

FPBits<T> to_bits(to);
if (to_bits.is_nan())
return to;

if (from == to)
return to;

using UIntType = typename FPBits<T>::UIntType;
UIntType int_val = from_bits.uintval();
UIntType sign_mask = (UIntType(1) << (sizeof(T) * 8 - 1));
if (from != T(0.0)) {
if ((from < to) == (from > T(0.0))) {
++int_val;
} else {
--int_val;
}
} else {
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) {
template <
typename T, typename U,
cpp::enable_if_t<cpp::is_floating_point_v<T> && cpp::is_floating_point_v<U>,
int> = 0>
LIBC_INLINE T nextafter(T from, U to) {
FPBits<T> from_bits(from);
if (from_bits.is_nan())
return from;

FPBits<long double> to_bits(to);
FPBits<U> to_bits(to);
if (to_bits.is_nan())
return to;
return static_cast<T>(to);

if ((long double)from == to)
return to;
if (static_cast<U>(from) == to)
Copy link
Contributor

@nishantwrp nishantwrp Nov 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a side note: This would work only if U is equal or higher precision than T. (Which is OK for our usecase but should we add a comment or maybe some condition in enable_if that ensures this?)

cc: @michaelrj-google @lntue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be good, yes. I'd say adding both a comment and a condition in the enableif such as sizeof(T) >= sizeof(U) would be the ideal solution.

return static_cast<T>(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))) {
if (from != FPBits<T>::zero()) {
if ((static_cast<U>(from) < to) == (from > FPBits<T>::zero())) {
++int_val;
} else {
--int_val;
Expand Down
4 changes: 3 additions & 1 deletion libc/src/math/generic/nexttoward.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(double, nexttoward, (double x, long double y)) {
return fputil::nexttoward(x, y);
// We can reuse the nextafter implementation because the internal nextafter is
// templated on the types of the arguments.
return fputil::nextafter(x, y);
}

} // namespace LIBC_NAMESPACE
4 changes: 3 additions & 1 deletion libc/src/math/generic/nexttowardf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
namespace LIBC_NAMESPACE {

LLVM_LIBC_FUNCTION(float, nexttowardf, (float x, long double y)) {
return fputil::nexttoward(x, y);
// We can reuse the nextafter implementation because the internal nextafter is
// templated on the types of the arguments.
return fputil::nextafter(x, y);
}

} // namespace LIBC_NAMESPACE
5 changes: 2 additions & 3 deletions libc/src/math/generic/nexttowardl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@
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.
// We can reuse the nextafter implementation because the internal nextafter is
// templated on the types of the arguments.
return fputil::nextafter(x, y);
}

Expand Down