Skip to content

[libc][math] Add Generic Comparison Operations for floating point types #144983

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
11 changes: 11 additions & 0 deletions libc/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,17 @@ add_header_library(
libc.src.__support.macros.properties.types
)

add_header_library(
comparison_operations
HDRS
ComparisonOperations.h
DEPENDS
.fp_bits
.fenv_impl
libc.src.__support.CPP.type_traits
libc.src.__support.config
Comment on lines +217 to +220
Copy link
Member

Choose a reason for hiding this comment

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

Nit: sorting.

Suggested change
.fp_bits
.fenv_impl
libc.src.__support.CPP.type_traits
libc.src.__support.config
.fenv_impl
.fp_bits
libc.src.__support.config
libc.src.__support.CPP.type_traits

)

add_header_library(
hypot
HDRS
Expand Down
122 changes: 122 additions & 0 deletions libc/src/__support/FPUtil/ComparisonOperations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//===-- Comparison operations on floating point numbers --------*- C++ -*-===//
Copy link
Member

Choose a reason for hiding this comment

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

Nit: formatting.

Suggested change
//===-- Comparison operations on floating point numbers --------*- C++ -*-===//
//===-- Comparison operations on floating point 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_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H
#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H

#include "FEnvImpl.h" // raise_except_if_required
#include "FPBits.h" // FPBits<T>
#include "src/__support/CPP/type_traits.h" // enable_if, is_floating_point
#include "src/__support/macros/config.h" // LIBC_NAMESPACE_DECL

namespace LIBC_NAMESPACE_DECL {
namespace fputil {

// IEEE Standard 754-2019. Section 5.11
// Rules for comparison within the same floating point type
// 1. +0 = −0
// 2. (i) +inf = +inf
// (ii) -inf = -inf
// (iii) -inf != +inf
// 3. Any comparison with NaN return false except (NaN != NaN => true)
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> equals(T x,
T y) {
using FPBits = FPBits<T>;
FPBits x_bits(x);
FPBits y_bits(y);

if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);

// NaN == x returns false for every x
if (x_bits.is_nan() || y_bits.is_nan())
return false;

// +/- 0 == +/- 0
if (x_bits.is_zero() && y_bits.is_zero())
return true;

// should also work for comparisons of different signs
return x_bits.uintval() == y_bits.uintval();
}

// !(x == y) => x != y
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
not_equals(T x, T y) {
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need not_equals? The PR is not adding functions for the other "NOT <relation>" predicates (compareSignalingNotGreater, compareSignalingLessUnordered, compareSignalingNotLess, compareSignalingGreaterUnordered).

return !equals(x, y);
}

// Rules:
// 1. -inf < x (x != -inf)
// 2. x < +inf (x != +inf)
// 3. Any comparison with NaN return false
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool> less_than(T x,
T y) {
using FPBits = FPBits<T>;
FPBits x_bits(x);
FPBits y_bits(y);

if (x_bits.is_signaling_nan() || y_bits.is_signaling_nan())
fputil::raise_except_if_required(FE_INVALID);
Comment on lines +67 to +68
Copy link
Member

Choose a reason for hiding this comment

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

Comparisons other than equal and not equal should also raise FE_INVALID for quiet NaN operands. See note after table 5.1 in IEEE Std 754-2019:

[...] The Signaling predicates in Table 5.1 signal the invalid operation exception on quiet NaN operands to warn of potential incorrect behavior of programs written assuming trichotomy.


// Any comparison with NaN returns false
if (x_bits.is_nan() || y_bits.is_nan())
return false;

if (x_bits.is_zero() && y_bits.is_zero())
return false;

if (x_bits.is_neg() && y_bits.is_pos())
return true;

if (x_bits.is_pos() && y_bits.is_neg())
return false;

// since we store the float in the format: s | e | m
// the comparisons should work if we directly compare the uintval's

// TODO: verify if we should use FPBits.get_exponent and FPBits.get_mantissa
// instead of directly comparing uintval's

// both negative
if (x_bits.is_neg())
return x_bits.uintval() > y_bits.uintval();

// both positive
return x_bits.uintval() < y_bits.uintval();
}

// x < y => y > x
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
greater_than(T x, T y) {
return less_than(y, x);
}

// following is expression is correct, accounting for NaN case(s) as well
// x <= y => (x < y) || (x == y)
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
less_than_or_equals(T x, T y) {
return less_than(x, y) || equals(x, y);
}

// x >= y => (x > y) || (x == y) => (y < x) || (x == y)
template <typename T>
LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
greater_than_or_equals(T x, T y) {
return less_than(y, x) || equals(x, y);
}

} // namespace fputil
} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_COMPARISONOPERATIONS_H
12 changes: 12 additions & 0 deletions libc/test/src/__support/FPUtil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,15 @@ add_fp_unittest(
DEPENDS
libc.src.__support.FPUtil.rounding_mode
)

add_fp_unittest(
comparison_operations_test
SUITE
libc-fputil-tests
SRCS
comparison_operations_test.cpp
DEPENDS
libc.src.__support.FPUtil.fp_bits
# libc.src.__support.FPUtil.comparison_operations
Comment on lines +48 to +50
Copy link
Member

Choose a reason for hiding this comment

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

Dependencies should be updated.

)

Copy link
Member

Choose a reason for hiding this comment

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

Nit: extra line break.

Loading
Loading