@@ -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 );
@@ -7440,4 +7515,79 @@ TEST(APFloatTest, Float4E2M1FNToFloat) {
74407515 EXPECT_TRUE (SmallestDenorm.isDenormal ());
74417516 EXPECT_EQ (0x0 .8p0, SmallestDenorm.convertToFloat ());
74427517}
7518+
7519+ TEST (APFloatTest, AddOrSubtractSignificand) {
7520+ class TestIEEEFloat : detail::IEEEFloat {
7521+ TestIEEEFloat (bool sign, ExponentType exponent, integerPart significand)
7522+ : detail::IEEEFloat(1.0 ) {
7523+ // `addOrSubtractSignificand` only uses the sign, exponent and significand
7524+ this ->sign = sign;
7525+ this ->exponent = exponent;
7526+ this ->significand .part = significand;
7527+ }
7528+
7529+ public:
7530+ static void runTest (bool subtract, bool lhsSign, ExponentType lhsExponent,
7531+ integerPart lhsSignificand, bool rhsSign,
7532+ ExponentType rhsExponent, integerPart rhsSignificand,
7533+ bool expectedSign, ExponentType expectedExponent,
7534+ integerPart expectedSignificand,
7535+ lostFraction expectedLoss) {
7536+ TestIEEEFloat lhs (lhsSign, lhsExponent, lhsSignificand);
7537+ TestIEEEFloat rhs (rhsSign, rhsExponent, rhsSignificand);
7538+ lostFraction resultLoss = lhs.addOrSubtractSignificand (rhs, subtract);
7539+ EXPECT_EQ (resultLoss, expectedLoss);
7540+ EXPECT_EQ (lhs.sign , expectedSign);
7541+ EXPECT_EQ (lhs.exponent , expectedExponent);
7542+ EXPECT_EQ (lhs.significand .part , expectedSignificand);
7543+ }
7544+ };
7545+
7546+ // Test cases are all combinations of:
7547+ // {equal exponents, LHS larger exponent, RHS larger exponent}
7548+ // {equal significands, LHS larger significand, RHS larger significand}
7549+ // {no loss, loss}
7550+
7551+ // Equal exponents (loss cannot occur as their is no shifting)
7552+ TestIEEEFloat::runTest (true , false , 1 , 0x10 , false , 1 , 0x5 , false , 1 , 0xb ,
7553+ lfExactlyZero);
7554+ TestIEEEFloat::runTest (false , false , -2 , 0x20 , true , -2 , 0x20 , false , -2 , 0 ,
7555+ lfExactlyZero);
7556+ TestIEEEFloat::runTest (false , true , 3 , 0x20 , false , 3 , 0x30 , false , 3 , 0x10 ,
7557+ lfExactlyZero);
7558+
7559+ // LHS larger exponent
7560+ // LHS significand greater after shitfing
7561+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x100 , false , 6 ,
7562+ 0x1e0 , lfExactlyZero);
7563+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x101 , false , 6 ,
7564+ 0x1df , lfMoreThanHalf);
7565+ // Significands equal after shitfing
7566+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x1000 , false , 6 , 0 ,
7567+ lfExactlyZero);
7568+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x1001 , true , 6 , 0 ,
7569+ lfLessThanHalf);
7570+ // RHS significand greater after shitfing
7571+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x10000 , true , 6 ,
7572+ 0x1e00 , lfExactlyZero);
7573+ TestIEEEFloat::runTest (true , false , 7 , 0x100 , false , 3 , 0x10001 , true , 6 ,
7574+ 0x1e00 , lfLessThanHalf);
7575+
7576+ // RHS larger exponent
7577+ // RHS significand greater after shitfing
7578+ TestIEEEFloat::runTest (true , false , 3 , 0x100 , false , 7 , 0x100 , true , 6 , 0x1e0 ,
7579+ lfExactlyZero);
7580+ TestIEEEFloat::runTest (true , false , 3 , 0x101 , false , 7 , 0x100 , true , 6 , 0x1df ,
7581+ lfMoreThanHalf);
7582+ // Significands equal after shitfing
7583+ TestIEEEFloat::runTest (true , false , 3 , 0x1000 , false , 7 , 0x100 , false , 6 , 0 ,
7584+ lfExactlyZero);
7585+ TestIEEEFloat::runTest (true , false , 3 , 0x1001 , false , 7 , 0x100 , false , 6 , 0 ,
7586+ lfLessThanHalf);
7587+ // LHS significand greater after shitfing
7588+ TestIEEEFloat::runTest (true , false , 3 , 0x10000 , false , 7 , 0x100 , false , 6 ,
7589+ 0x1e00 , lfExactlyZero);
7590+ TestIEEEFloat::runTest (true , false , 3 , 0x10001 , false , 7 , 0x100 , false , 6 ,
7591+ 0x1e00 , lfLessThanHalf);
7592+ }
74437593} // namespace
0 commit comments