@@ -560,6 +560,104 @@ TEST(APFloatTest, FMA) {
560
560
EXPECT_EQ(-8.85242279E-41f, f1.convertToFloat());
561
561
}
562
562
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
+
563
661
// Test using only a single instance of APFloat.
564
662
{
565
663
APFloat F(1.5);
@@ -8089,4 +8187,82 @@ TEST(APFloatTest, Float4E2M1FNToFloat) {
8089
8187
EXPECT_TRUE(SmallestDenorm.isDenormal());
8090
8188
EXPECT_EQ(0x0.8p0, SmallestDenorm.convertToFloat());
8091
8189
}
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
+ }
8092
8268
} // namespace
0 commit comments