diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h index c24eae8da3797..db2fa480655c6 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` before diff --git a/llvm/unittests/ADT/APFloatTest.cpp b/llvm/unittests/ADT/APFloatTest.cpp index f6af4b0e5f651..cf6bbd313c6c6 100644 --- a/llvm/unittests/ADT/APFloatTest.cpp +++ b/llvm/unittests/ADT/APFloatTest.cpp @@ -631,6 +631,100 @@ 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); + 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_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()); + EXPECT_TRUE(minimumnum(zn, zn).isNegative()); + EXPECT_FALSE(minimumnum(zp, zp).isNegative()); + + 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) { + APFloat f1(1.0); + APFloat f2(2.0); + APFloat zp(0.0); + APFloat zn(-0.0); + 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_TRUE(f2.bitwiseIsEqual(maximumnum(f1, f2))); + EXPECT_TRUE(f2.bitwiseIsEqual(maximumnum(f2, f1))); + EXPECT_TRUE(zp.bitwiseIsEqual(maximumnum(zp, zn))); + EXPECT_TRUE(zp.bitwiseIsEqual(maximumnum(zn, zp))); + + EXPECT_FALSE(maximumnum(zn, zp).isNegative()); + EXPECT_FALSE(maximumnum(zp, zn).isNegative()); + EXPECT_TRUE(maximumnum(zn, zn).isNegative()); + EXPECT_FALSE(maximumnum(zp, zp).isNegative()); + + 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) { APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;