@@ -47,6 +47,37 @@ static std::string convertToString(double d, unsigned Prec, unsigned Pad,
4747 return std::string (Buffer.data (), Buffer.size ());
4848}
4949
50+ namespace llvm {
51+ namespace detail {
52+ class IEEEFloatUnitTestHelper {
53+ public:
54+ static void runTest (bool subtract, bool lhsSign,
55+ APFloat::ExponentType lhsExponent,
56+ APFloat::integerPart lhsSignificand, bool rhsSign,
57+ APFloat::ExponentType rhsExponent,
58+ APFloat::integerPart rhsSignificand, bool expectedSign,
59+ APFloat::ExponentType expectedExponent,
60+ APFloat::integerPart expectedSignificand,
61+ lostFraction expectedLoss) {
62+ // `addOrSubtractSignificand` only uses the sign, exponent and significand
63+ IEEEFloat lhs (1.0 );
64+ lhs.sign = lhsSign;
65+ lhs.exponent = lhsExponent;
66+ lhs.significand .part = lhsSignificand;
67+ IEEEFloat rhs (1.0 );
68+ rhs.sign = rhsSign;
69+ rhs.exponent = rhsExponent;
70+ rhs.significand .part = rhsSignificand;
71+ lostFraction resultLoss = lhs.addOrSubtractSignificand (rhs, subtract);
72+ EXPECT_EQ (resultLoss, expectedLoss);
73+ EXPECT_EQ (lhs.sign , expectedSign);
74+ EXPECT_EQ (lhs.exponent , expectedExponent);
75+ EXPECT_EQ (lhs.significand .part , expectedSignificand);
76+ }
77+ };
78+ } // namespace detail
79+ } // namespace llvm
80+
5081namespace {
5182
5283TEST (APFloatTest, isSignaling) {
@@ -560,6 +591,104 @@ TEST(APFloatTest, FMA) {
560591 EXPECT_EQ (-8 .85242279E-41f , f1.convertToFloat ());
561592 }
562593
594+ // The `addOrSubtractSignificand` can be considered to have 9 possible cases
595+ // when subtracting: all combinations of {cmpLessThan, cmpGreaterThan,
596+ // cmpEqual} and {no loss, loss from lhs, loss from rhs}. Test each reachable
597+ // case here.
598+
599+ // Regression test for failing the `assert(!carry)` in
600+ // `addOrSubtractSignificand` and normalizing the exponent even when the
601+ // significand is zero if there is a lost fraction.
602+ // This tests cmpEqual, loss from lhs
603+ {
604+ APFloat f1 (-1 .4728589E-38f );
605+ APFloat f2 (3 .7105144E-6f );
606+ APFloat f3 (5 .5E-44f );
607+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
608+ EXPECT_EQ (-0 .0f , f1.convertToFloat ());
609+ }
610+
611+ // Test cmpGreaterThan, no loss
612+ {
613+ APFloat f1 (2 .0f );
614+ APFloat f2 (2 .0f );
615+ APFloat f3 (-3 .5f );
616+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
617+ EXPECT_EQ (0 .5f , f1.convertToFloat ());
618+ }
619+
620+ // Test cmpLessThan, no loss
621+ {
622+ APFloat f1 (2 .0f );
623+ APFloat f2 (2 .0f );
624+ APFloat f3 (-4 .5f );
625+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
626+ EXPECT_EQ (-0 .5f , f1.convertToFloat ());
627+ }
628+
629+ // Test cmpEqual, no loss
630+ {
631+ APFloat f1 (2 .0f );
632+ APFloat f2 (2 .0f );
633+ APFloat f3 (-4 .0f );
634+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
635+ EXPECT_EQ (0 .0f , f1.convertToFloat ());
636+ }
637+
638+ // Test cmpLessThan, loss from lhs
639+ {
640+ APFloat f1 (2 .0000002f );
641+ APFloat f2 (2 .0000002f );
642+ APFloat f3 (-32 .0f );
643+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
644+ EXPECT_EQ (-27 .999998f , f1.convertToFloat ());
645+ }
646+
647+ // Test cmpGreaterThan, loss from rhs
648+ {
649+ APFloat f1 (1e10f);
650+ APFloat f2 (1e10f);
651+ APFloat f3 (-2 .0000002f );
652+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
653+ EXPECT_EQ (1e20f, f1.convertToFloat ());
654+ }
655+
656+ // Test cmpGreaterThan, loss from lhs
657+ {
658+ APFloat f1 (1e-36f );
659+ APFloat f2 (0 .0019531252f );
660+ APFloat f3 (-1e-45f );
661+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
662+ EXPECT_EQ (1 .953124e-39f , f1.convertToFloat ());
663+ }
664+
665+ // {cmpEqual, cmpLessThan} with loss from rhs can't occur for the usage in
666+ // `fusedMultiplyAdd` as `multiplySignificand` normalises the MSB of lhs to
667+ // one bit below the top.
668+
669+ // Test cases from #104984
670+ {
671+ APFloat f1 (0 .24999998f );
672+ APFloat f2 (2 .3509885e-38f );
673+ APFloat f3 (-1e-45f );
674+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
675+ EXPECT_EQ (5 .87747e-39f , f1.convertToFloat ());
676+ }
677+ {
678+ APFloat f1 (4.4501477170144023e-308 );
679+ APFloat f2 (0.24999999999999997 );
680+ APFloat f3 (-8.475904604373977e-309 );
681+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
682+ EXPECT_EQ (2.64946468816203e-309 , f1.convertToDouble ());
683+ }
684+ {
685+ APFloat f1 (APFloat::IEEEhalf (), APInt (16 , 0x8fffu ));
686+ APFloat f2 (APFloat::IEEEhalf (), APInt (16 , 0x2bffu ));
687+ APFloat f3 (APFloat::IEEEhalf (), APInt (16 , 0x0172u ));
688+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
689+ EXPECT_EQ (0x808eu , f1.bitcastToAPInt ().getZExtValue ());
690+ }
691+
563692 // Test using only a single instance of APFloat.
564693 {
565694 APFloat F (1.5 );
@@ -8168,4 +8297,54 @@ TEST(APFloatTest, Float4E2M1FNToFloat) {
81688297 EXPECT_TRUE (SmallestDenorm.isDenormal ());
81698298 EXPECT_EQ (0x0 .8p0, SmallestDenorm.convertToFloat ());
81708299}
8300+
8301+ TEST (APFloatTest, AddOrSubtractSignificand) {
8302+ typedef detail::IEEEFloatUnitTestHelper Helper;
8303+ // Test cases are all combinations of:
8304+ // {equal exponents, LHS larger exponent, RHS larger exponent}
8305+ // {equal significands, LHS larger significand, RHS larger significand}
8306+ // {no loss, loss}
8307+
8308+ // Equal exponents (loss cannot occur as their is no shifting)
8309+ Helper::runTest (true , false , 1 , 0x10 , false , 1 , 0x5 , false , 1 , 0xb ,
8310+ lfExactlyZero);
8311+ Helper::runTest (false , false , -2 , 0x20 , true , -2 , 0x20 , false , -2 , 0 ,
8312+ lfExactlyZero);
8313+ Helper::runTest (false , true , 3 , 0x20 , false , 3 , 0x30 , false , 3 , 0x10 ,
8314+ lfExactlyZero);
8315+
8316+ // LHS larger exponent
8317+ // LHS significand greater after shitfing
8318+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x100 , false , 6 , 0x1e0 ,
8319+ lfExactlyZero);
8320+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x101 , false , 6 , 0x1df ,
8321+ lfMoreThanHalf);
8322+ // Significands equal after shitfing
8323+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x1000 , false , 6 , 0 ,
8324+ lfExactlyZero);
8325+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x1001 , true , 6 , 0 ,
8326+ lfLessThanHalf);
8327+ // RHS significand greater after shitfing
8328+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x10000 , true , 6 , 0x1e00 ,
8329+ lfExactlyZero);
8330+ Helper::runTest (true , false , 7 , 0x100 , false , 3 , 0x10001 , true , 6 , 0x1e00 ,
8331+ lfLessThanHalf);
8332+
8333+ // RHS larger exponent
8334+ // RHS significand greater after shitfing
8335+ Helper::runTest (true , false , 3 , 0x100 , false , 7 , 0x100 , true , 6 , 0x1e0 ,
8336+ lfExactlyZero);
8337+ Helper::runTest (true , false , 3 , 0x101 , false , 7 , 0x100 , true , 6 , 0x1df ,
8338+ lfMoreThanHalf);
8339+ // Significands equal after shitfing
8340+ Helper::runTest (true , false , 3 , 0x1000 , false , 7 , 0x100 , false , 6 , 0 ,
8341+ lfExactlyZero);
8342+ Helper::runTest (true , false , 3 , 0x1001 , false , 7 , 0x100 , false , 6 , 0 ,
8343+ lfLessThanHalf);
8344+ // LHS significand greater after shitfing
8345+ Helper::runTest (true , false , 3 , 0x10000 , false , 7 , 0x100 , false , 6 , 0x1e00 ,
8346+ lfExactlyZero);
8347+ Helper::runTest (true , false , 3 , 0x10001 , false , 7 , 0x100 , false , 6 , 0x1e00 ,
8348+ lfLessThanHalf);
8349+ }
81718350} // namespace
0 commit comments