@@ -560,6 +560,104 @@ 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+
638+ // Test cases from #104984
639+ {
640+ APFloat f1 (0 .24999998f );
641+ APFloat f2 (2 .3509885e-38f );
642+ APFloat f3 (-1e-45f );
643+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
644+ EXPECT_EQ (5 .87747e-39f , f1.convertToFloat ());
645+ }
646+ {
647+ APFloat f1 (4.4501477170144023e-308 );
648+ APFloat f2 (0.24999999999999997 );
649+ APFloat f3 (-8.475904604373977e-309 );
650+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
651+ EXPECT_EQ (2.64946468816203e-309 , f1.convertToDouble ());
652+ }
653+ {
654+ APFloat f1 (APFloat::IEEEhalf (), APInt (16 , 0x8fffu ));
655+ APFloat f2 (APFloat::IEEEhalf (), APInt (16 , 0x2bffu ));
656+ APFloat f3 (APFloat::IEEEhalf (), APInt (16 , 0x0172u ));
657+ f1.fusedMultiplyAdd (f2, f3, APFloat::rmNearestTiesToEven);
658+ EXPECT_EQ (0x808eu , f1.bitcastToAPInt ().getZExtValue ());
659+ }
660+
563661 // Test using only a single instance of APFloat.
564662 {
565663 APFloat F (1.5 );
@@ -8089,4 +8187,82 @@ TEST(APFloatTest, Float4E2M1FNToFloat) {
80898187 EXPECT_TRUE (SmallestDenorm.isDenormal ());
80908188 EXPECT_EQ (0x0 .8p0, SmallestDenorm.convertToFloat ());
80918189}
8190+
8191+ TEST (APFloatTest, AddOrSubtractSignificand) {
8192+ class TestIEEEFloat : detail::IEEEFloat {
8193+ TestIEEEFloat (bool sign, APFloat::ExponentType exponent,
8194+ APFloat::integerPart significand)
8195+ : detail::IEEEFloat(1.0 ) {
8196+ // `addOrSubtractSignificand` only uses the sign, exponent and significand
8197+ this ->sign = sign;
8198+ this ->exponent = exponent;
8199+ this ->significand .part = significand;
8200+ }
8201+
8202+ public:
8203+ static void runTest (bool subtract, bool lhsSign,
8204+ APFloat::ExponentType lhsExponent,
8205+ APFloat::integerPart lhsSignificand, bool rhsSign,
8206+ APFloat::ExponentType rhsExponent,
8207+ APFloat::integerPart rhsSignificand, bool expectedSign,
8208+ APFloat::ExponentType expectedExponent,
8209+ APFloat::integerPart expectedSignificand,
8210+ lostFraction expectedLoss) {
8211+ TestIEEEFloat lhs (lhsSign, lhsExponent, lhsSignificand);
8212+ TestIEEEFloat rhs (rhsSign, rhsExponent, rhsSignificand);
8213+ lostFraction resultLoss = lhs.addOrSubtractSignificand (rhs, subtract);
8214+ EXPECT_EQ (resultLoss, expectedLoss);
8215+ EXPECT_EQ (lhs.sign , expectedSign);
8216+ EXPECT_EQ (lhs.exponent , expectedExponent);
8217+ EXPECT_EQ (lhs.significand .part , expectedSignificand);
8218+ }
8219+ };
8220+
8221+ // Test cases are all combinations of:
8222+ // {equal exponents, LHS larger exponent, RHS larger exponent}
8223+ // {equal significands, LHS larger significand, RHS larger significand}
8224+ // {no loss, loss}
8225+
8226+ // Equal exponents (loss cannot occur as their is no shifting)
8227+ TestIEEEFloat::runTest (true , false , 1 , 0x10 , false , 1 , 0x5 , false , 1 , 0xb ,
8228+ lfExactlyZero);
8229+ TestIEEEFloat::runTest (false , false , -2 , 0x20 , true , -2 , 0x20 , false , -2 , 0 ,
8230+ lfExactlyZero);
8231+ TestIEEEFloat::runTest (false , true , 3 , 0x20 , false , 3 , 0x30 , false , 3 , 0x10 ,
8232+ lfExactlyZero);
8233+
8234+ // LHS larger exponent
8235+ // LHS significand greater after shitfing
8236+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x100 , false , 6 ,
8237+ 0x1e0 , lfExactlyZero);
8238+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x101 , false , 6 ,
8239+ 0x1df , lfMoreThanHalf);
8240+ // Significands equal after shitfing
8241+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x1000 , false , 6 , 0 ,
8242+ lfExactlyZero);
8243+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x1001 , true , 6 , 0 ,
8244+ lfLessThanHalf);
8245+ // RHS significand greater after shitfing
8246+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x10000 , true , 6 ,
8247+ 0x1e00 , lfExactlyZero);
8248+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x10001 , true , 6 ,
8249+ 0x1e00 , lfLessThanHalf);
8250+
8251+ // RHS larger exponent
8252+ // RHS significand greater after shitfing
8253+ TestIEEEFloat::runTest (true , false , 3 , 0x100 , false , 7 , 0x100 , true , 6 , 0x1e0 ,
8254+ lfExactlyZero);
8255+ TestIEEEFloat::runTest (true , false , 3 , 0x101 , false , 7 , 0x100 , true , 6 , 0x1df ,
8256+ lfMoreThanHalf);
8257+ // Significands equal after shitfing
8258+ TestIEEEFloat::runTest (true , false , 3 , 0x1000 , false , 7 , 0x100 , false , 6 , 0 ,
8259+ lfExactlyZero);
8260+ TestIEEEFloat::runTest (true , false , 3 , 0x1001 , false , 7 , 0x100 , false , 6 , 0 ,
8261+ lfLessThanHalf);
8262+ // LHS significand greater after shitfing
8263+ TestIEEEFloat::runTest (true , false , 3 , 0x10000 , false , 7 , 0x100 , false , 6 ,
8264+ 0x1e00 , lfExactlyZero);
8265+ TestIEEEFloat::runTest (true , false , 3 , 0x10001 , false , 7 , 0x100 , false , 6 ,
8266+ 0x1e00 , lfLessThanHalf);
8267+ }
80928268} // namespace
0 commit comments