@@ -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