Skip to content

Commit 3f36539

Browse files
committed
[APFloat] Fix IEEEFloat::addOrSubtractSignificand and IEEEFloat::normalize
1 parent 7d1b6b2 commit 3f36539

File tree

2 files changed

+111
-15
lines changed

2 files changed

+111
-15
lines changed

llvm/lib/Support/APFloat.cpp

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,7 +1607,8 @@ IEEEFloat::opStatus IEEEFloat::normalize(roundingMode rounding_mode,
16071607
/* Before rounding normalize the exponent of fcNormal numbers. */
16081608
omsb = significandMSB() + 1;
16091609

1610-
if (omsb) {
1610+
// Only skip this `if` if the value is exactly zero.
1611+
if (omsb || lost_fraction != lfExactlyZero) {
16111612
/* OMSB is numbered from 1. We want to place it in the integer
16121613
bit numbered PRECISION if possible, with a compensating change in
16131614
the exponent. */
@@ -1782,7 +1783,7 @@ IEEEFloat::opStatus IEEEFloat::addOrSubtractSpecials(const IEEEFloat &rhs,
17821783
/* Add or subtract two normal numbers. */
17831784
lostFraction IEEEFloat::addOrSubtractSignificand(const IEEEFloat &rhs,
17841785
bool subtract) {
1785-
integerPart carry;
1786+
integerPart carry = 0;
17861787
lostFraction lost_fraction;
17871788
int bits;
17881789

@@ -1796,35 +1797,55 @@ lostFraction IEEEFloat::addOrSubtractSignificand(const IEEEFloat &rhs,
17961797
/* Subtraction is more subtle than one might naively expect. */
17971798
if (subtract) {
17981799
IEEEFloat temp_rhs(rhs);
1800+
bool lost_fraction_is_from_rhs = false;
17991801

18001802
if (bits == 0)
18011803
lost_fraction = lfExactlyZero;
18021804
else if (bits > 0) {
18031805
lost_fraction = temp_rhs.shiftSignificandRight(bits - 1);
1806+
lost_fraction_is_from_rhs = true;
18041807
shiftSignificandLeft(1);
18051808
} else {
18061809
lost_fraction = shiftSignificandRight(-bits - 1);
18071810
temp_rhs.shiftSignificandLeft(1);
18081811
}
18091812

18101813
// Should we reverse the subtraction.
1811-
if (compareAbsoluteValue(temp_rhs) == cmpLessThan) {
1812-
carry = temp_rhs.subtractSignificand
1813-
(*this, lost_fraction != lfExactlyZero);
1814+
cmpResult cmp_result = compareAbsoluteValue(temp_rhs);
1815+
if (cmp_result == cmpLessThan) {
1816+
bool borrow = false;
1817+
if (lost_fraction != lfExactlyZero && !lost_fraction_is_from_rhs) {
1818+
// The lost fraction is being subtracted, borrow from the significand
1819+
// and invert `lost_fraction`.
1820+
borrow = true;
1821+
if (lost_fraction == lfLessThanHalf)
1822+
lost_fraction = lfMoreThanHalf;
1823+
else if (lost_fraction == lfMoreThanHalf)
1824+
lost_fraction = lfLessThanHalf;
1825+
}
1826+
carry = temp_rhs.subtractSignificand(*this, borrow);
18141827
copySignificand(temp_rhs);
18151828
sign = !sign;
1816-
} else {
1817-
carry = subtractSignificand
1818-
(temp_rhs, lost_fraction != lfExactlyZero);
1829+
} else if (cmp_result == cmpGreaterThan) {
1830+
bool borrow = false;
1831+
if (lost_fraction != lfExactlyZero && lost_fraction_is_from_rhs) {
1832+
// The lost fraction is being subtracted, borrow from the significand
1833+
// and invert `lost_fraction`.
1834+
borrow = true;
1835+
if (lost_fraction == lfLessThanHalf)
1836+
lost_fraction = lfMoreThanHalf;
1837+
else if (lost_fraction == lfMoreThanHalf)
1838+
lost_fraction = lfLessThanHalf;
1839+
}
1840+
carry = subtractSignificand(temp_rhs, borrow);
1841+
} else { // cmpEqual
1842+
zeroSignificand();
1843+
if (lost_fraction != lfExactlyZero && lost_fraction_is_from_rhs) {
1844+
// rhs is slightly larger due to the lost fraction, flip the sign.
1845+
sign = !sign;
1846+
}
18191847
}
18201848

1821-
/* Invert the lost fraction - it was on the RHS and
1822-
subtracted. */
1823-
if (lost_fraction == lfLessThanHalf)
1824-
lost_fraction = lfMoreThanHalf;
1825-
else if (lost_fraction == lfMoreThanHalf)
1826-
lost_fraction = lfLessThanHalf;
1827-
18281849
/* The code above is intended to ensure that no borrow is
18291850
necessary. */
18301851
assert(!carry);

llvm/unittests/ADT/APFloatTest.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,81 @@ TEST(APFloatTest, FMA) {
560560
EXPECT_EQ(-8.85242279E-41f, f1.convertToFloat());
561561
}
562562

563+
// The `addOrSubtractSignificand` can be considered to have 9 possible cases
564+
// when subtracting: all combinations of {cmpLessThan, cmpGreaterThan,
565+
// cmpEqual} and {no loss, loss from lhs, loss from rhs}. Test each reachable
566+
// case here.
567+
568+
// Regression test for failing the `assert(!carry)` in
569+
// `addOrSubtractSignificand` and normalizing the exponent even when the
570+
// significand is zero if there is a lost fraction.
571+
// This tests cmpEqual, loss from lhs
572+
{
573+
APFloat f1(-1.4728589E-38f);
574+
APFloat f2(3.7105144E-6f);
575+
APFloat f3(5.5E-44f);
576+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
577+
EXPECT_EQ(-0.0f, f1.convertToFloat());
578+
}
579+
580+
// Test cmpGreaterThan, no loss
581+
{
582+
APFloat f1(2.0f);
583+
APFloat f2(2.0f);
584+
APFloat f3(-3.5f);
585+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
586+
EXPECT_EQ(0.5f, f1.convertToFloat());
587+
}
588+
589+
// Test cmpLessThan, no loss
590+
{
591+
APFloat f1(2.0f);
592+
APFloat f2(2.0f);
593+
APFloat f3(-4.5f);
594+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
595+
EXPECT_EQ(-0.5f, f1.convertToFloat());
596+
}
597+
598+
// Test cmpEqual, no loss
599+
{
600+
APFloat f1(2.0f);
601+
APFloat f2(2.0f);
602+
APFloat f3(-4.0f);
603+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
604+
EXPECT_EQ(0.0f, f1.convertToFloat());
605+
}
606+
607+
// Test cmpLessThan, loss from lhs
608+
{
609+
APFloat f1(2.0000002f);
610+
APFloat f2(2.0000002f);
611+
APFloat f3(-32.0f);
612+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
613+
EXPECT_EQ(-27.999998f, f1.convertToFloat());
614+
}
615+
616+
// Test cmpGreaterThan, loss from rhs
617+
{
618+
APFloat f1(1e10f);
619+
APFloat f2(1e10f);
620+
APFloat f3(-2.0000002f);
621+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
622+
EXPECT_EQ(1e20f, f1.convertToFloat());
623+
}
624+
625+
// Test cmpGreaterThan, loss from lhs
626+
{
627+
APFloat f1(1e-36f);
628+
APFloat f2(0.0019531252f);
629+
APFloat f3(-1e-45f);
630+
f1.fusedMultiplyAdd(f2, f3, APFloat::rmNearestTiesToEven);
631+
EXPECT_EQ(1.953124e-39f, f1.convertToFloat());
632+
}
633+
634+
// {cmpEqual, cmpLessThan} with loss from rhs can't occur for the usage in
635+
// `fusedMultiplyAdd` as `multiplySignificand` normalises the MSB of lhs to
636+
// one bit below the top.
637+
563638
// Test using only a single instance of APFloat.
564639
{
565640
APFloat F(1.5);

0 commit comments

Comments
 (0)