[llvm] APFloat: Add minimumnum and maximumnum (PR #96304)

YunQiang Su via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 25 08:12:36 PDT 2024


https://github.com/wzssyqa updated https://github.com/llvm/llvm-project/pull/96304

>From d3f2af2e61aa5585730bed0d419f52738aa770bc Mon Sep 17 00:00:00 2001
From: YunQiang Su <syq at gcc.gnu.org>
Date: Fri, 21 Jun 2024 21:24:39 +0800
Subject: [PATCH 1/3] APFloat: Add minimumnum and maximumnum

They implements IEEE754-2019 minimumNumber and maximumNumber
semantics.

Newer libc also has these 2 function with name
   fminimum_num
   fmaximum_num

We are planning add minimumnum and maximumnum intrinsic.
This is a step for them.
---
 llvm/include/llvm/ADT/APFloat.h    | 26 ++++++++++++++++
 llvm/unittests/ADT/APFloatTest.cpp | 48 ++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)

diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index c24eae8da3797b..db2fa480655c61 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1483,6 +1483,19 @@ inline APFloat minimum(const APFloat &A, const APFloat &B) {
   return B < A ? B : A;
 }
 
+/// Implements IEEE 754-2019 minimumNumber semantics. Returns the smaller
+/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
+LLVM_READONLY
+inline APFloat minimumnum(const APFloat &A, const APFloat &B) {
+  if (A.isNaN())
+    return B.isNaN() ? B.makeQuiet() : B;
+  if (B.isNaN())
+    return A;
+  if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
+    return A.isNegative() ? A : B;
+  return B < A ? B : A;
+}
+
 /// Implements IEEE 754-2019 maximum semantics. Returns the larger of 2
 /// arguments, propagating NaNs and treating -0 as less than +0.
 LLVM_READONLY
@@ -1496,6 +1509,19 @@ inline APFloat maximum(const APFloat &A, const APFloat &B) {
   return A < B ? B : A;
 }
 
+/// Implements IEEE 754-2019 maximumNumber semantics. Returns the larger
+/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
+LLVM_READONLY
+inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
+  if (A.isNaN())
+    return B.isNaN() ? B.makeQuiet() : B;
+  if (B.isNaN())
+    return A;
+  if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
+    return A.isNegative() ? B : A;
+  return A < B ? B : A;
+}
+
 // We want the following functions to be available in the header for inlining.
 // We cannot define them inline in the class definition of `DoubleAPFloat`
 // because doing so would instantiate `std::unique_ptr<APFloat[]>` before
diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index f6af4b0e5f6512..a105cd81d74312 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -631,6 +631,54 @@ TEST(APFloatTest, Maximum) {
   EXPECT_TRUE(std::isnan(maximum(nan, f1).convertToDouble()));
 }
 
+TEST(APFloatTest, MinimumNumber) {
+  APFloat f1(1.0);
+  APFloat f2(2.0);
+  APFloat zp(0.0);
+  APFloat zn(-0.0);
+  APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
+  APFloat snan = APFloat::getSNaN(APFloat::IEEEdouble());
+
+  EXPECT_EQ(1.0, minimumnum(f1, f2).convertToDouble());
+  EXPECT_EQ(1.0, minimumnum(f2, f1).convertToDouble());
+  EXPECT_EQ(-0.0, minimumnum(zp, zn).convertToDouble());
+  EXPECT_EQ(-0.0, minimumnum(zn, zp).convertToDouble());
+  EXPECT_TRUE(minimumnum(zn, zp).isNegative());
+  EXPECT_TRUE(minimumnum(zp, zn).isNegative());
+  EXPECT_TRUE(minimumnum(zn, zn).isNegative());
+  EXPECT_FALSE(minimumnum(zp, zp).isNegative());
+  EXPECT_FALSE(std::isnan(minimumnum(f1, nan).convertToDouble()));
+  EXPECT_FALSE(std::isnan(minimumnum(nan, f1).convertToDouble()));
+  EXPECT_FALSE(std::isnan(minimumnum(f1, snan).convertToDouble()));
+  EXPECT_FALSE(std::isnan(minimumnum(snan, f1).convertToDouble()));
+  EXPECT_FALSE(minimumnum(snan, nan).isSignaling());
+  EXPECT_FALSE(minimumnum(snan, snan).isSignaling());
+}
+
+TEST(APFloatTest, MaximumNumber) {
+  APFloat f1(1.0);
+  APFloat f2(2.0);
+  APFloat zp(0.0);
+  APFloat zn(-0.0);
+  APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
+  APFloat snan = APFloat::getSNaN(APFloat::IEEEdouble());
+
+  EXPECT_EQ(2.0, maximumnum(f1, f2).convertToDouble());
+  EXPECT_EQ(2.0, maximumnum(f2, f1).convertToDouble());
+  EXPECT_EQ(0.0, maximumnum(zp, zn).convertToDouble());
+  EXPECT_EQ(0.0, maximumnum(zn, zp).convertToDouble());
+  EXPECT_FALSE(maximumnum(zn, zp).isNegative());
+  EXPECT_FALSE(maximumnum(zp, zn).isNegative());
+  EXPECT_TRUE(maximumnum(zn, zn).isNegative());
+  EXPECT_FALSE(maximumnum(zp, zp).isNegative());
+  EXPECT_FALSE(std::isnan(maximumnum(f1, nan).convertToDouble()));
+  EXPECT_FALSE(std::isnan(maximumnum(nan, f1).convertToDouble()));
+  EXPECT_FALSE(std::isnan(maximumnum(f1, snan).convertToDouble()));
+  EXPECT_FALSE(std::isnan(maximumnum(snan, f1).convertToDouble()));
+  EXPECT_FALSE(maximumnum(snan, nan).isSignaling());
+  EXPECT_FALSE(maximumnum(snan, snan).isSignaling());
+}
+
 TEST(APFloatTest, Denormal) {
   APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;
 

>From e6a679486bebdc3fc353179ab5066044cdcc642e Mon Sep 17 00:00:00 2001
From: YunQiang Su <syq at gcc.gnu.org>
Date: Sat, 22 Jun 2024 13:46:14 +0800
Subject: [PATCH 2/3] Add more unittest

---
 llvm/unittests/ADT/APFloatTest.cpp | 78 ++++++++++++++++++++++++------
 1 file changed, 62 insertions(+), 16 deletions(-)

diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index a105cd81d74312..481ef96912a2dc 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -636,23 +636,46 @@ TEST(APFloatTest, MinimumNumber) {
   APFloat f2(2.0);
   APFloat zp(0.0);
   APFloat zn(-0.0);
-  APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
-  APFloat snan = APFloat::getSNaN(APFloat::IEEEdouble());
+  APInt intPayload_89ab(64, 0x89ab);
+  APInt intPayload_cdef(64, 0xcdef);
+  APFloat nan_0123[2] = {APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123),
+                         APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123)};
+  APFloat mnan_4567[2] = {APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567),
+                          APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567)};
+  APFloat nan_89ab[2] = {
+      APFloat::getSNaN(APFloat::IEEEdouble(), false, &intPayload_89ab),
+      APFloat::getNaN(APFloat::IEEEdouble(), false, 0x89ab)};
+  APFloat mnan_cdef[2] = {
+      APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
+      APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
 
   EXPECT_EQ(1.0, minimumnum(f1, f2).convertToDouble());
   EXPECT_EQ(1.0, minimumnum(f2, f1).convertToDouble());
   EXPECT_EQ(-0.0, minimumnum(zp, zn).convertToDouble());
   EXPECT_EQ(-0.0, minimumnum(zn, zp).convertToDouble());
+
   EXPECT_TRUE(minimumnum(zn, zp).isNegative());
   EXPECT_TRUE(minimumnum(zp, zn).isNegative());
   EXPECT_TRUE(minimumnum(zn, zn).isNegative());
   EXPECT_FALSE(minimumnum(zp, zp).isNegative());
-  EXPECT_FALSE(std::isnan(minimumnum(f1, nan).convertToDouble()));
-  EXPECT_FALSE(std::isnan(minimumnum(nan, f1).convertToDouble()));
-  EXPECT_FALSE(std::isnan(minimumnum(f1, snan).convertToDouble()));
-  EXPECT_FALSE(std::isnan(minimumnum(snan, f1).convertToDouble()));
-  EXPECT_FALSE(minimumnum(snan, nan).isSignaling());
-  EXPECT_FALSE(minimumnum(snan, snan).isSignaling());
+
+  for (APFloat n : {nan_0123[0], mnan_4567[0], nan_89ab[0], mnan_cdef[0]})
+    for (APFloat f : {f1, f2, zn, zp}) {
+      APFloat res = minimumnum(f, n);
+      EXPECT_FALSE(res.isNaN());
+      EXPECT_TRUE(res.bitwiseIsEqual(f));
+      res = minimumnum(n, f);
+      EXPECT_FALSE(res.isNaN());
+      EXPECT_TRUE(res.bitwiseIsEqual(f));
+    }
+
+  // When NaN vs NaN, we should keep payload/sign of either one.
+  for (auto n1 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef})
+    for (auto n2 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef}) {
+      APFloat res = minimumnum(n1[0], n2[0]);
+      EXPECT_TRUE(res.bitwiseIsEqual(n1[1]) || res.bitwiseIsEqual(n2[1]));
+      EXPECT_FALSE(res.isSignaling());
+    }
 }
 
 TEST(APFloatTest, MaximumNumber) {
@@ -660,23 +683,46 @@ TEST(APFloatTest, MaximumNumber) {
   APFloat f2(2.0);
   APFloat zp(0.0);
   APFloat zn(-0.0);
-  APFloat nan = APFloat::getNaN(APFloat::IEEEdouble());
-  APFloat snan = APFloat::getSNaN(APFloat::IEEEdouble());
+  APInt intPayload_89ab(64, 0x89ab);
+  APInt intPayload_cdef(64, 0xcdef);
+  APFloat nan_0123[2] = {APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123),
+                         APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123)};
+  APFloat mnan_4567[2] = {APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567),
+                          APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567)};
+  APFloat nan_89ab[2] = {
+      APFloat::getSNaN(APFloat::IEEEdouble(), false, &intPayload_89ab),
+      APFloat::getNaN(APFloat::IEEEdouble(), false, 0x89ab)};
+  APFloat mnan_cdef[2] = {
+      APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
+      APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
 
   EXPECT_EQ(2.0, maximumnum(f1, f2).convertToDouble());
   EXPECT_EQ(2.0, maximumnum(f2, f1).convertToDouble());
   EXPECT_EQ(0.0, maximumnum(zp, zn).convertToDouble());
   EXPECT_EQ(0.0, maximumnum(zn, zp).convertToDouble());
+
   EXPECT_FALSE(maximumnum(zn, zp).isNegative());
   EXPECT_FALSE(maximumnum(zp, zn).isNegative());
   EXPECT_TRUE(maximumnum(zn, zn).isNegative());
   EXPECT_FALSE(maximumnum(zp, zp).isNegative());
-  EXPECT_FALSE(std::isnan(maximumnum(f1, nan).convertToDouble()));
-  EXPECT_FALSE(std::isnan(maximumnum(nan, f1).convertToDouble()));
-  EXPECT_FALSE(std::isnan(maximumnum(f1, snan).convertToDouble()));
-  EXPECT_FALSE(std::isnan(maximumnum(snan, f1).convertToDouble()));
-  EXPECT_FALSE(maximumnum(snan, nan).isSignaling());
-  EXPECT_FALSE(maximumnum(snan, snan).isSignaling());
+
+  for (APFloat n : {nan_0123[0], mnan_4567[0], nan_89ab[0], mnan_cdef[0]})
+    for (APFloat f : {f1, f2, zn, zp}) {
+      APFloat res = maximumnum(f, n);
+      EXPECT_FALSE(res.isNaN());
+      EXPECT_TRUE(res.bitwiseIsEqual(f));
+      res = maximumnum(n, f);
+      EXPECT_FALSE(res.isNaN());
+      EXPECT_TRUE(res.bitwiseIsEqual(f));
+    }
+
+  // When NaN vs NaN, we should keep payload/sign of either one.
+  for (auto n1 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef})
+    for (auto n2 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef}) {
+      APFloat res = maximumnum(n1[0], n2[0]);
+      EXPECT_TRUE(res.bitwiseIsEqual(n1[1]) || res.bitwiseIsEqual(n2[1]));
+      EXPECT_FALSE(res.isSignaling());
+    }
 }
 
 TEST(APFloatTest, Denormal) {

>From 57681e8dbb3012f131701d57cfbb8192588db536 Mon Sep 17 00:00:00 2001
From: YunQiang Su <syq at gcc.gnu.org>
Date: Tue, 25 Jun 2024 23:12:04 +0800
Subject: [PATCH 3/3] use bitwiseIsEqual to replace convertToDouble

---
 llvm/unittests/ADT/APFloatTest.cpp | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp
index 481ef96912a2dc..8bd465c869bcdc 100644
--- a/llvm/unittests/ADT/APFloatTest.cpp
+++ b/llvm/unittests/ADT/APFloatTest.cpp
@@ -649,10 +649,10 @@ TEST(APFloatTest, MinimumNumber) {
       APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
       APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
 
-  EXPECT_EQ(1.0, minimumnum(f1, f2).convertToDouble());
-  EXPECT_EQ(1.0, minimumnum(f2, f1).convertToDouble());
-  EXPECT_EQ(-0.0, minimumnum(zp, zn).convertToDouble());
-  EXPECT_EQ(-0.0, minimumnum(zn, zp).convertToDouble());
+  EXPECT_TRUE(f1.bitwiseIsEqual(minimumnum(f1, f2)));
+  EXPECT_TRUE(f1.bitwiseIsEqual(minimumnum(f2, f1)));
+  EXPECT_TRUE(zn.bitwiseIsEqual(minimumnum(zp, zn)));
+  EXPECT_TRUE(zn.bitwiseIsEqual(minimumnum(zn, zp)));
 
   EXPECT_TRUE(minimumnum(zn, zp).isNegative());
   EXPECT_TRUE(minimumnum(zp, zn).isNegative());
@@ -696,10 +696,10 @@ TEST(APFloatTest, MaximumNumber) {
       APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
       APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
 
-  EXPECT_EQ(2.0, maximumnum(f1, f2).convertToDouble());
-  EXPECT_EQ(2.0, maximumnum(f2, f1).convertToDouble());
-  EXPECT_EQ(0.0, maximumnum(zp, zn).convertToDouble());
-  EXPECT_EQ(0.0, maximumnum(zn, zp).convertToDouble());
+  EXPECT_TRUE(f2.bitwiseIsEqual(minimumnum(f1, f2)));
+  EXPECT_TRUE(f2.bitwiseIsEqual(minimumnum(f2, f1)));
+  EXPECT_TRUE(zp.bitwiseIsEqual(minimumnum(zp, zn)));
+  EXPECT_TRUE(zp.bitwiseIsEqual(minimumnum(zn, zp)));
 
   EXPECT_FALSE(maximumnum(zn, zp).isNegative());
   EXPECT_FALSE(maximumnum(zp, zn).isNegative());



More information about the llvm-commits mailing list