xref: /aosp_15_r20/external/llvm-libc/test/src/math/smoke/FmaTest.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1 //===-- Utility class to test different flavors of fma --------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_LIBC_TEST_SRC_MATH_FMATEST_H
10 #define LLVM_LIBC_TEST_SRC_MATH_FMATEST_H
11 
12 #include "src/__support/CPP/type_traits.h"
13 #include "src/__support/FPUtil/cast.h"
14 #include "src/__support/macros/properties/types.h"
15 #include "test/UnitTest/FEnvSafeTest.h"
16 #include "test/UnitTest/FPMatcher.h"
17 #include "test/UnitTest/Test.h"
18 
19 template <typename OutType, typename InType = OutType>
20 class FmaTestTemplate : public LIBC_NAMESPACE::testing::FEnvSafeTest {
21 
22   struct OutConstants {
23     DECLARE_SPECIAL_CONSTANTS(OutType)
24   };
25 
26   struct InConstants {
27     DECLARE_SPECIAL_CONSTANTS(InType)
28   };
29 
30   using OutFPBits = typename OutConstants::FPBits;
31   using OutStorageType = typename OutConstants::StorageType;
32   using InFPBits = typename InConstants::FPBits;
33   using InStorageType = typename InConstants::StorageType;
34 
35   static constexpr OutStorageType OUT_MIN_NORMAL_U =
36       OutFPBits::min_normal().uintval();
37   static constexpr InStorageType IN_MIN_NORMAL_U =
38       InFPBits::min_normal().uintval();
39 
40   OutConstants out;
41   InConstants in;
42 
43   const InType in_out_min_normal =
44       LIBC_NAMESPACE::fputil::cast<InType>(out.min_normal);
45   const InType in_out_min_denormal =
46       LIBC_NAMESPACE::fputil::cast<InType>(out.min_denormal);
47 
48 public:
49   using FmaFunc = OutType (*)(InType, InType, InType);
50 
test_special_numbers(FmaFunc func)51   void test_special_numbers(FmaFunc func) {
52     EXPECT_FP_EQ(out.zero, func(in.zero, in.zero, in.zero));
53     EXPECT_FP_EQ(out.neg_zero, func(in.zero, in.neg_zero, in.neg_zero));
54     EXPECT_FP_EQ(out.inf, func(in.inf, in.inf, in.zero));
55     EXPECT_FP_EQ(out.neg_inf, func(in.neg_inf, in.inf, in.neg_inf));
56     EXPECT_FP_EQ(out.aNaN, func(in.inf, in.zero, in.zero));
57     EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.inf));
58     EXPECT_FP_EQ(out.aNaN, func(in.aNaN, in.zero, in.inf));
59     EXPECT_FP_EQ(out.aNaN, func(in.inf, in.neg_inf, in.aNaN));
60 
61     // Test underflow rounding up.
62     EXPECT_FP_EQ(OutFPBits(OutStorageType(2)).get_val(),
63                  func(InType(0.5), in_out_min_denormal, in_out_min_denormal));
64 
65     if constexpr (sizeof(OutType) < sizeof(InType)) {
66       EXPECT_FP_EQ(out.zero,
67                    func(InType(0.5), in.min_denormal, in.min_denormal));
68     }
69 
70     // Test underflow rounding down.
71     OutType v = OutFPBits(static_cast<OutStorageType>(OUT_MIN_NORMAL_U +
72                                                       OutStorageType(1)))
73                     .get_val();
74     EXPECT_FP_EQ(v, func(InType(1) / InType(OUT_MIN_NORMAL_U << 1),
75                          LIBC_NAMESPACE::fputil::cast<InType>(v),
76                          in_out_min_normal));
77 
78     if constexpr (sizeof(OutType) < sizeof(InType)) {
79       InFPBits tmp = InFPBits::one();
80       tmp.set_biased_exponent(InFPBits::EXP_BIAS - InFPBits::FRACTION_LEN - 1);
81       InType reciprocal_value = tmp.get_val();
82 
83       InType v = InFPBits(static_cast<InStorageType>(IN_MIN_NORMAL_U +
84                                                      InStorageType(1)))
85                      .get_val();
86       EXPECT_FP_EQ(out.min_normal,
87                    func(reciprocal_value, v, in_out_min_normal));
88     }
89 
90     // Test overflow.
91     OutType z = out.max_normal;
92     InType in_z = LIBC_NAMESPACE::fputil::cast<InType>(out.max_normal);
93 #if defined(LIBC_TYPES_HAS_FLOAT16) && !defined(__LIBC_USE_FLOAT16_CONVERSION)
94     // Rounding modes other than the default might not be usable with float16.
95     if constexpr (LIBC_NAMESPACE::cpp::is_same_v<OutType, float16>)
96       EXPECT_FP_EQ(OutType(0.75) * z, func(InType(1.75), in_z, -in_z));
97     else
98 #endif
99       EXPECT_FP_EQ_ALL_ROUNDING(OutType(0.75) * z,
100                                 func(InType(1.75), in_z, -in_z));
101 
102     // Exact cancellation.
103     EXPECT_FP_EQ_ROUNDING_NEAREST(
104         out.zero, func(InType(3.0), InType(5.0), InType(-15.0)));
105     EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero,
106                                  func(InType(3.0), InType(5.0), InType(-15.0)));
107     EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
108         out.zero, func(InType(3.0), InType(5.0), InType(-15.0)));
109     EXPECT_FP_EQ_ROUNDING_DOWNWARD(
110         out.neg_zero, func(InType(3.0), InType(5.0), InType(-15.0)));
111 
112     EXPECT_FP_EQ_ROUNDING_NEAREST(
113         out.zero, func(InType(-3.0), InType(5.0), InType(15.0)));
114     EXPECT_FP_EQ_ROUNDING_UPWARD(out.zero,
115                                  func(InType(-3.0), InType(5.0), InType(15.0)));
116     EXPECT_FP_EQ_ROUNDING_TOWARD_ZERO(
117         out.zero, func(InType(-3.0), InType(5.0), InType(15.0)));
118     EXPECT_FP_EQ_ROUNDING_DOWNWARD(
119         out.neg_zero, func(InType(-3.0), InType(5.0), InType(15.0)));
120   }
121 };
122 
123 #define LIST_FMA_TESTS(T, func)                                                \
124   using LlvmLibcFmaTest = FmaTestTemplate<T>;                                  \
125   TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); }
126 
127 #define LIST_NARROWING_FMA_TESTS(OutType, InType, func)                        \
128   using LlvmLibcFmaTest = FmaTestTemplate<OutType, InType>;                    \
129   TEST_F(LlvmLibcFmaTest, SpecialNumbers) { test_special_numbers(&func); }
130 
131 #endif // LLVM_LIBC_TEST_SRC_MATH_FMATEST_H
132