Skip to content

Commit 0d53366

Browse files
authored
APFloat: Add minimumnum and maximumnum (#96304)
They implements IEEE754-2019 minimumNumber and maximumNumber semantics. Newer libc also has these 2 functions with names fminimum_num fmaximum_num We are planning add minimumnum and maximumnum intrinsic. This is a step to the goal.
1 parent 847235b commit 0d53366

File tree

2 files changed

+120
-0
lines changed

2 files changed

+120
-0
lines changed

llvm/include/llvm/ADT/APFloat.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,19 @@ inline APFloat minimum(const APFloat &A, const APFloat &B) {
14831483
return B < A ? B : A;
14841484
}
14851485

1486+
/// Implements IEEE 754-2019 minimumNumber semantics. Returns the smaller
1487+
/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
1488+
LLVM_READONLY
1489+
inline APFloat minimumnum(const APFloat &A, const APFloat &B) {
1490+
if (A.isNaN())
1491+
return B.isNaN() ? B.makeQuiet() : B;
1492+
if (B.isNaN())
1493+
return A;
1494+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1495+
return A.isNegative() ? A : B;
1496+
return B < A ? B : A;
1497+
}
1498+
14861499
/// Implements IEEE 754-2019 maximum semantics. Returns the larger of 2
14871500
/// arguments, propagating NaNs and treating -0 as less than +0.
14881501
LLVM_READONLY
@@ -1496,6 +1509,19 @@ inline APFloat maximum(const APFloat &A, const APFloat &B) {
14961509
return A < B ? B : A;
14971510
}
14981511

1512+
/// Implements IEEE 754-2019 maximumNumber semantics. Returns the larger
1513+
/// of 2 arguments, not propagating NaNs and treating -0 as less than +0.
1514+
LLVM_READONLY
1515+
inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
1516+
if (A.isNaN())
1517+
return B.isNaN() ? B.makeQuiet() : B;
1518+
if (B.isNaN())
1519+
return A;
1520+
if (A.isZero() && B.isZero() && (A.isNegative() != B.isNegative()))
1521+
return A.isNegative() ? B : A;
1522+
return A < B ? B : A;
1523+
}
1524+
14991525
// We want the following functions to be available in the header for inlining.
15001526
// We cannot define them inline in the class definition of `DoubleAPFloat`
15011527
// because doing so would instantiate `std::unique_ptr<APFloat[]>` before

llvm/unittests/ADT/APFloatTest.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,100 @@ TEST(APFloatTest, Maximum) {
631631
EXPECT_TRUE(std::isnan(maximum(nan, f1).convertToDouble()));
632632
}
633633

634+
TEST(APFloatTest, MinimumNumber) {
635+
APFloat f1(1.0);
636+
APFloat f2(2.0);
637+
APFloat zp(0.0);
638+
APFloat zn(-0.0);
639+
APInt intPayload_89ab(64, 0x89ab);
640+
APInt intPayload_cdef(64, 0xcdef);
641+
APFloat nan_0123[2] = {APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123),
642+
APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123)};
643+
APFloat mnan_4567[2] = {APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567),
644+
APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567)};
645+
APFloat nan_89ab[2] = {
646+
APFloat::getSNaN(APFloat::IEEEdouble(), false, &intPayload_89ab),
647+
APFloat::getNaN(APFloat::IEEEdouble(), false, 0x89ab)};
648+
APFloat mnan_cdef[2] = {
649+
APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
650+
APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
651+
652+
EXPECT_TRUE(f1.bitwiseIsEqual(minimumnum(f1, f2)));
653+
EXPECT_TRUE(f1.bitwiseIsEqual(minimumnum(f2, f1)));
654+
EXPECT_TRUE(zn.bitwiseIsEqual(minimumnum(zp, zn)));
655+
EXPECT_TRUE(zn.bitwiseIsEqual(minimumnum(zn, zp)));
656+
657+
EXPECT_TRUE(minimumnum(zn, zp).isNegative());
658+
EXPECT_TRUE(minimumnum(zp, zn).isNegative());
659+
EXPECT_TRUE(minimumnum(zn, zn).isNegative());
660+
EXPECT_FALSE(minimumnum(zp, zp).isNegative());
661+
662+
for (APFloat n : {nan_0123[0], mnan_4567[0], nan_89ab[0], mnan_cdef[0]})
663+
for (APFloat f : {f1, f2, zn, zp}) {
664+
APFloat res = minimumnum(f, n);
665+
EXPECT_FALSE(res.isNaN());
666+
EXPECT_TRUE(res.bitwiseIsEqual(f));
667+
res = minimumnum(n, f);
668+
EXPECT_FALSE(res.isNaN());
669+
EXPECT_TRUE(res.bitwiseIsEqual(f));
670+
}
671+
672+
// When NaN vs NaN, we should keep payload/sign of either one.
673+
for (auto n1 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef})
674+
for (auto n2 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef}) {
675+
APFloat res = minimumnum(n1[0], n2[0]);
676+
EXPECT_TRUE(res.bitwiseIsEqual(n1[1]) || res.bitwiseIsEqual(n2[1]));
677+
EXPECT_FALSE(res.isSignaling());
678+
}
679+
}
680+
681+
TEST(APFloatTest, MaximumNumber) {
682+
APFloat f1(1.0);
683+
APFloat f2(2.0);
684+
APFloat zp(0.0);
685+
APFloat zn(-0.0);
686+
APInt intPayload_89ab(64, 0x89ab);
687+
APInt intPayload_cdef(64, 0xcdef);
688+
APFloat nan_0123[2] = {APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123),
689+
APFloat::getNaN(APFloat::IEEEdouble(), false, 0x0123)};
690+
APFloat mnan_4567[2] = {APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567),
691+
APFloat::getNaN(APFloat::IEEEdouble(), true, 0x4567)};
692+
APFloat nan_89ab[2] = {
693+
APFloat::getSNaN(APFloat::IEEEdouble(), false, &intPayload_89ab),
694+
APFloat::getNaN(APFloat::IEEEdouble(), false, 0x89ab)};
695+
APFloat mnan_cdef[2] = {
696+
APFloat::getSNaN(APFloat::IEEEdouble(), true, &intPayload_cdef),
697+
APFloat::getNaN(APFloat::IEEEdouble(), true, 0xcdef)};
698+
699+
EXPECT_TRUE(f2.bitwiseIsEqual(maximumnum(f1, f2)));
700+
EXPECT_TRUE(f2.bitwiseIsEqual(maximumnum(f2, f1)));
701+
EXPECT_TRUE(zp.bitwiseIsEqual(maximumnum(zp, zn)));
702+
EXPECT_TRUE(zp.bitwiseIsEqual(maximumnum(zn, zp)));
703+
704+
EXPECT_FALSE(maximumnum(zn, zp).isNegative());
705+
EXPECT_FALSE(maximumnum(zp, zn).isNegative());
706+
EXPECT_TRUE(maximumnum(zn, zn).isNegative());
707+
EXPECT_FALSE(maximumnum(zp, zp).isNegative());
708+
709+
for (APFloat n : {nan_0123[0], mnan_4567[0], nan_89ab[0], mnan_cdef[0]})
710+
for (APFloat f : {f1, f2, zn, zp}) {
711+
APFloat res = maximumnum(f, n);
712+
EXPECT_FALSE(res.isNaN());
713+
EXPECT_TRUE(res.bitwiseIsEqual(f));
714+
res = maximumnum(n, f);
715+
EXPECT_FALSE(res.isNaN());
716+
EXPECT_TRUE(res.bitwiseIsEqual(f));
717+
}
718+
719+
// When NaN vs NaN, we should keep payload/sign of either one.
720+
for (auto n1 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef})
721+
for (auto n2 : {nan_0123, mnan_4567, nan_89ab, mnan_cdef}) {
722+
APFloat res = maximumnum(n1[0], n2[0]);
723+
EXPECT_TRUE(res.bitwiseIsEqual(n1[1]) || res.bitwiseIsEqual(n2[1]));
724+
EXPECT_FALSE(res.isSignaling());
725+
}
726+
}
727+
634728
TEST(APFloatTest, Denormal) {
635729
APFloat::roundingMode rdmd = APFloat::rmNearestTiesToEven;
636730

0 commit comments

Comments
 (0)