@@ -47,6 +47,37 @@ static std::string convertToString(double d, unsigned Prec, unsigned Pad,
47
47
return std::string(Buffer.data(), Buffer.size());
48
48
}
49
49
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
+
50
81
namespace {
51
82
52
83
TEST(APFloatTest, isSignaling) {
@@ -560,6 +591,104 @@ TEST(APFloatTest, FMA) {
560
591
EXPECT_EQ(-8.85242279E-41f, f1.convertToFloat());
561
592
}
562
593
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
+
563
692
// Test using only a single instance of APFloat.
564
693
{
565
694
APFloat F(1.5);
@@ -8168,4 +8297,54 @@ TEST(APFloatTest, Float4E2M1FNToFloat) {
8168
8297
EXPECT_TRUE(SmallestDenorm.isDenormal());
8169
8298
EXPECT_EQ(0x0.8p0, SmallestDenorm.convertToFloat());
8170
8299
}
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
+ }
8171
8350
} // namespace
0 commit comments