Skip to content

Commit 689181d

Browse files
majnemergithub-actions[bot]
authored andcommitted
Automerge: [APFloat] Properly implement DoubleAPFloat::compareAbsoluteValue
The prior implementation would treat X+Y and X-Y as having equal magnitude. Rework the implementation to be more resilient.
2 parents c798f43 + f961b61 commit 689181d

File tree

3 files changed

+148
-15
lines changed

3 files changed

+148
-15
lines changed

llvm/include/llvm/ADT/APFloat.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,8 @@ class APFloat : public APFloatBase {
10451045
explicit APFloat(DoubleAPFloat F, const fltSemantics &S)
10461046
: U(std::move(F), S) {}
10471047

1048+
// Compares the absolute value of this APFloat with another. Both operands
1049+
// must be finite non-zero.
10481050
cmpResult compareAbsoluteValue(const APFloat &RHS) const {
10491051
assert(&getSemantics() == &RHS.getSemantics() &&
10501052
"Should only compare APFloats with the same semantics");
@@ -1408,6 +1410,8 @@ class APFloat : public APFloatBase {
14081410
return Res == cmpGreaterThan || Res == cmpEqual;
14091411
}
14101412

1413+
// IEEE comparison with another floating point number (NaNs compare unordered,
1414+
// 0==-0).
14111415
cmpResult compare(const APFloat &RHS) const {
14121416
assert(&getSemantics() == &RHS.getSemantics() &&
14131417
"Should only compare APFloats with the same semantics");

llvm/lib/Support/APFloat.cpp

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5285,23 +5285,56 @@ void DoubleAPFloat::changeSign() {
52855285

52865286
APFloat::cmpResult
52875287
DoubleAPFloat::compareAbsoluteValue(const DoubleAPFloat &RHS) const {
5288-
auto Result = Floats[0].compareAbsoluteValue(RHS.Floats[0]);
5289-
if (Result != cmpEqual)
5290-
return Result;
5291-
Result = Floats[1].compareAbsoluteValue(RHS.Floats[1]);
5292-
if (Result == cmpLessThan || Result == cmpGreaterThan) {
5293-
auto Against = Floats[0].isNegative() ^ Floats[1].isNegative();
5294-
auto RHSAgainst = RHS.Floats[0].isNegative() ^ RHS.Floats[1].isNegative();
5295-
if (Against && !RHSAgainst)
5296-
return cmpLessThan;
5297-
if (!Against && RHSAgainst)
5288+
// Compare absolute values of the high parts.
5289+
const cmpResult HiPartCmp = Floats[0].compareAbsoluteValue(RHS.Floats[0]);
5290+
if (HiPartCmp != cmpEqual)
5291+
return HiPartCmp;
5292+
5293+
// Zero, regardless of sign, is equal.
5294+
if (Floats[1].isZero() && RHS.Floats[1].isZero())
5295+
return cmpEqual;
5296+
5297+
// At this point, |this->Hi| == |RHS.Hi|.
5298+
// The magnitude is |Hi+Lo| which is Hi+|Lo| if signs of Hi and Lo are the
5299+
// same, and Hi-|Lo| if signs are different.
5300+
const bool ThisIsSubtractive =
5301+
Floats[0].isNegative() != Floats[1].isNegative();
5302+
const bool RHSIsSubtractive =
5303+
RHS.Floats[0].isNegative() != RHS.Floats[1].isNegative();
5304+
5305+
// Case 1: The low part of 'this' is zero.
5306+
if (Floats[1].isZero())
5307+
// We are comparing |Hi| vs. |Hi| ± |RHS.Lo|.
5308+
// If RHS is subtractive, its magnitude is smaller.
5309+
// If RHS is additive, its magnitude is larger.
5310+
return RHSIsSubtractive ? cmpGreaterThan : cmpLessThan;
5311+
5312+
// Case 2: The low part of 'RHS' is zero (and we know 'this' is not).
5313+
if (RHS.Floats[1].isZero())
5314+
// We are comparing |Hi| ± |This.Lo| vs. |Hi|.
5315+
// If 'this' is subtractive, its magnitude is smaller.
5316+
// If 'this' is additive, its magnitude is larger.
5317+
return ThisIsSubtractive ? cmpLessThan : cmpGreaterThan;
5318+
5319+
// If their natures differ, the additive one is larger.
5320+
if (ThisIsSubtractive != RHSIsSubtractive)
5321+
return ThisIsSubtractive ? cmpLessThan : cmpGreaterThan;
5322+
5323+
// Case 3: Both are additive (Hi+|Lo|) or both are subtractive (Hi-|Lo|).
5324+
// The comparison now depends on the magnitude of the low parts.
5325+
const cmpResult LoPartCmp = Floats[1].compareAbsoluteValue(RHS.Floats[1]);
5326+
5327+
if (ThisIsSubtractive) {
5328+
// Both are subtractive (Hi-|Lo|), so the comparison of |Lo| is inverted.
5329+
if (LoPartCmp == cmpLessThan)
52985330
return cmpGreaterThan;
5299-
if (!Against && !RHSAgainst)
5300-
return Result;
5301-
if (Against && RHSAgainst)
5302-
return (cmpResult)(cmpLessThan + cmpGreaterThan - Result);
5331+
if (LoPartCmp == cmpGreaterThan)
5332+
return cmpLessThan;
53035333
}
5304-
return Result;
5334+
5335+
// If additive, the comparison of |Lo| is direct.
5336+
// If equal, they are equal.
5337+
return LoPartCmp;
53055338
}
53065339

53075340
APFloat::fltCategory DoubleAPFloat::getCategory() const {

llvm/unittests/ADT/APFloatTest.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6207,6 +6207,102 @@ TEST(APFloatTest, PPCDoubleDoubleCompare) {
62076207
}
62086208
}
62096209

6210+
namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails {
6211+
struct TestCase {
6212+
DD LHS;
6213+
DD RHS;
6214+
APFloat::cmpResult Result;
6215+
};
6216+
6217+
auto testCases() {
6218+
static constexpr auto CompareAbsoluteValueTestCases = std::array{
6219+
TestCase{
6220+
{1.0, 0.0},
6221+
{1.0, 0.0},
6222+
APFloat::cmpEqual,
6223+
},
6224+
TestCase{
6225+
{1.0, -0.0},
6226+
{1.0, +0.0},
6227+
APFloat::cmpEqual,
6228+
},
6229+
TestCase{
6230+
{1.0, 0.0},
6231+
{0x1.0000000000001p+0, 0.0},
6232+
APFloat::cmpLessThan,
6233+
},
6234+
TestCase{
6235+
{0x1.0000000000001p+0, 0.0},
6236+
{1.0, 0.0},
6237+
APFloat::cmpGreaterThan,
6238+
},
6239+
TestCase{
6240+
{0x1.0000000000001p+0, +0x1p-1074},
6241+
{1.0, -0x1p-1074},
6242+
APFloat::cmpGreaterThan,
6243+
},
6244+
TestCase{
6245+
{0x1.0000000000001p+0, -0x1p-1074},
6246+
{1.0, +0x1p-1074},
6247+
APFloat::cmpGreaterThan,
6248+
},
6249+
TestCase{
6250+
{1.0, 0.0},
6251+
{1.0, -0x1p-1074},
6252+
APFloat::cmpGreaterThan,
6253+
},
6254+
TestCase{
6255+
{1.0, 0.0},
6256+
{1.0, +0x1p-1074},
6257+
APFloat::cmpLessThan,
6258+
},
6259+
TestCase{
6260+
{1.0, +0x1p-1073},
6261+
{1.0, -0x1p-1074},
6262+
APFloat::cmpGreaterThan,
6263+
},
6264+
TestCase{
6265+
{1.0, +0x1p-1074},
6266+
{1.0, -0x1p-1074},
6267+
APFloat::cmpGreaterThan,
6268+
},
6269+
};
6270+
return CompareAbsoluteValueTestCases;
6271+
}
6272+
} // namespace PPCDoubleDoubleCompareAbsoluteValueTestDetails
6273+
6274+
class PPCDoubleDoubleCompareAbsoluteValueValueTest
6275+
: public testing::Test,
6276+
public ::testing::WithParamInterface<
6277+
PPCDoubleDoubleCompareAbsoluteValueTestDetails::TestCase> {};
6278+
6279+
INSTANTIATE_TEST_SUITE_P(
6280+
PPCDoubleDoubleCompareAbsoluteValueValueParamTests,
6281+
PPCDoubleDoubleCompareAbsoluteValueValueTest,
6282+
::testing::ValuesIn(
6283+
PPCDoubleDoubleCompareAbsoluteValueTestDetails::testCases()));
6284+
6285+
TEST_P(PPCDoubleDoubleCompareAbsoluteValueValueTest,
6286+
PPCDoubleDoubleCompareAbsoluteValue) {
6287+
auto Param = GetParam();
6288+
for (bool LHSNegate : {false, true}) {
6289+
auto LHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(),
6290+
APFloat{Param.LHS.Hi},
6291+
APFloat{Param.LHS.Lo}};
6292+
if (LHSNegate)
6293+
LHS.changeSign();
6294+
for (bool RHSNegate : {false, true}) {
6295+
auto RHS = llvm::detail::DoubleAPFloat{APFloat::PPCDoubleDouble(),
6296+
APFloat{Param.RHS.Hi},
6297+
APFloat{Param.RHS.Lo}};
6298+
if (RHSNegate)
6299+
RHS.changeSign();
6300+
6301+
EXPECT_EQ(LHS.compareAbsoluteValue(RHS), Param.Result);
6302+
}
6303+
}
6304+
}
6305+
62106306
TEST(APFloatTest, PPCDoubleDoubleBitwiseIsEqual) {
62116307
using DataType = std::tuple<uint64_t, uint64_t, uint64_t, uint64_t, bool>;
62126308

0 commit comments

Comments
 (0)