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