1 //===-- MPFRUtils.h ---------------------------------------------*- C++ -*-===// 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_UTILS_TESTUTILS_MPFRUTILS_H 10 #define LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 11 12 #include "src/__support/CPP/TypeTraits.h" 13 #include "utils/UnitTest/Test.h" 14 15 #include <stdint.h> 16 17 namespace __llvm_libc { 18 namespace testing { 19 namespace mpfr { 20 21 enum class Operation : int { 22 // Operations with take a single floating point number as input 23 // and produce a single floating point number as output. The input 24 // and output floating point numbers are of the same kind. 25 BeginUnaryOperationsSingleOutput, 26 Abs, 27 Ceil, 28 Cos, 29 Exp, 30 Exp2, 31 Expm1, 32 Floor, 33 Log, 34 Mod2PI, 35 ModPIOver2, 36 ModPIOver4, 37 Round, 38 Sin, 39 Sqrt, 40 Tan, 41 Trunc, 42 EndUnaryOperationsSingleOutput, 43 44 // Operations which take a single floating point nubmer as input 45 // but produce two outputs. The first ouput is a floating point 46 // number of the same type as the input. The second output is of type 47 // 'int'. 48 BeginUnaryOperationsTwoOutputs, 49 Frexp, // Floating point output, the first output, is the fractional part. 50 EndUnaryOperationsTwoOutputs, 51 52 // Operations wich take two floating point nubmers of the same type as 53 // input and produce a single floating point number of the same type as 54 // output. 55 BeginBinaryOperationsSingleOutput, 56 Hypot, 57 EndBinaryOperationsSingleOutput, 58 59 // Operations which take two floating point numbers of the same type as 60 // input and produce two outputs. The first output is a floating nubmer of 61 // the same type as the inputs. The second output is af type 'int'. 62 BeginBinaryOperationsTwoOutputs, 63 RemQuo, // The first output, the floating point output, is the remainder. 64 EndBinaryOperationsTwoOutputs, 65 66 // Operations which take three floating point nubmers of the same type as 67 // input and produce a single floating point number of the same type as 68 // output. 69 BeginTernaryOperationsSingleOuput, 70 Fma, 71 EndTernaryOperationsSingleOutput, 72 }; 73 74 template <typename T> struct BinaryInput { 75 static_assert( 76 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 77 "Template parameter of BinaryInput must be a floating point type."); 78 79 using Type = T; 80 T x, y; 81 }; 82 83 template <typename T> struct TernaryInput { 84 static_assert( 85 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 86 "Template parameter of TernaryInput must be a floating point type."); 87 88 using Type = T; 89 T x, y, z; 90 }; 91 92 template <typename T> struct BinaryOutput { 93 T f; 94 int i; 95 }; 96 97 namespace internal { 98 99 template <typename T1, typename T2> 100 struct AreMatchingBinaryInputAndBinaryOutput { 101 static constexpr bool VALUE = false; 102 }; 103 104 template <typename T> 105 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> { 106 static constexpr bool VALUE = cpp::IsFloatingPointType<T>::Value; 107 }; 108 109 template <typename T> 110 bool compare_unary_operation_single_output(Operation op, T input, T libc_output, 111 double t); 112 template <typename T> 113 bool compare_unary_operation_two_outputs(Operation op, T input, 114 const BinaryOutput<T> &libc_output, 115 double t); 116 template <typename T> 117 bool compare_binary_operation_two_outputs(Operation op, 118 const BinaryInput<T> &input, 119 const BinaryOutput<T> &libc_output, 120 double t); 121 122 template <typename T> 123 bool compare_binary_operation_one_output(Operation op, 124 const BinaryInput<T> &input, 125 T libc_output, double t); 126 127 template <typename T> 128 bool compare_ternary_operation_one_output(Operation op, 129 const TernaryInput<T> &input, 130 T libc_output, double t); 131 132 template <typename T> 133 void explain_unary_operation_single_output_error(Operation op, T input, 134 T match_value, 135 testutils::StreamWrapper &OS); 136 template <typename T> 137 void explain_unary_operation_two_outputs_error( 138 Operation op, T input, const BinaryOutput<T> &match_value, 139 testutils::StreamWrapper &OS); 140 template <typename T> 141 void explain_binary_operation_two_outputs_error( 142 Operation op, const BinaryInput<T> &input, 143 const BinaryOutput<T> &match_value, testutils::StreamWrapper &OS); 144 145 template <typename T> 146 void explain_binary_operation_one_output_error(Operation op, 147 const BinaryInput<T> &input, 148 T match_value, 149 testutils::StreamWrapper &OS); 150 151 template <typename T> 152 void explain_ternary_operation_one_output_error(Operation op, 153 const TernaryInput<T> &input, 154 T match_value, 155 testutils::StreamWrapper &OS); 156 157 template <Operation op, typename InputType, typename OutputType> 158 class MPFRMatcher : public testing::Matcher<OutputType> { 159 InputType input; 160 OutputType match_value; 161 double ulp_tolerance; 162 163 public: 164 MPFRMatcher(InputType testInput, double ulp_tolerance) 165 : input(testInput), ulp_tolerance(ulp_tolerance) {} 166 167 bool match(OutputType libcResult) { 168 match_value = libcResult; 169 return match(input, match_value, ulp_tolerance); 170 } 171 172 // This method is marked with NOLINT because it the name `explainError` 173 // does not confirm to the coding style. 174 void explainError(testutils::StreamWrapper &OS) override { // NOLINT 175 explain_error(input, match_value, OS); 176 } 177 178 private: 179 template <typename T> static bool match(T in, T out, double tolerance) { 180 return compare_unary_operation_single_output(op, in, out, tolerance); 181 } 182 183 template <typename T> 184 static bool match(T in, const BinaryOutput<T> &out, double tolerance) { 185 return compare_unary_operation_two_outputs(op, in, out, tolerance); 186 } 187 188 template <typename T> 189 static bool match(const BinaryInput<T> &in, T out, double tolerance) { 190 return compare_binary_operation_one_output(op, in, out, tolerance); 191 } 192 193 template <typename T> 194 static bool match(BinaryInput<T> in, const BinaryOutput<T> &out, 195 double tolerance) { 196 return compare_binary_operation_two_outputs(op, in, out, tolerance); 197 } 198 199 template <typename T> 200 static bool match(const TernaryInput<T> &in, T out, double tolerance) { 201 return compare_ternary_operation_one_output(op, in, out, tolerance); 202 } 203 204 template <typename T> 205 static void explain_error(T in, T out, testutils::StreamWrapper &OS) { 206 explain_unary_operation_single_output_error(op, in, out, OS); 207 } 208 209 template <typename T> 210 static void explain_error(T in, const BinaryOutput<T> &out, 211 testutils::StreamWrapper &OS) { 212 explain_unary_operation_two_outputs_error(op, in, out, OS); 213 } 214 215 template <typename T> 216 static void explain_error(const BinaryInput<T> &in, 217 const BinaryOutput<T> &out, 218 testutils::StreamWrapper &OS) { 219 explain_binary_operation_two_outputs_error(op, in, out, OS); 220 } 221 222 template <typename T> 223 static void explain_error(const BinaryInput<T> &in, T out, 224 testutils::StreamWrapper &OS) { 225 explain_binary_operation_one_output_error(op, in, out, OS); 226 } 227 228 template <typename T> 229 static void explain_error(const TernaryInput<T> &in, T out, 230 testutils::StreamWrapper &OS) { 231 explain_ternary_operation_one_output_error(op, in, out, OS); 232 } 233 }; 234 235 } // namespace internal 236 237 // Return true if the input and ouput types for the operation op are valid 238 // types. 239 template <Operation op, typename InputType, typename OutputType> 240 constexpr bool is_valid_operation() { 241 return (Operation::BeginUnaryOperationsSingleOutput < op && 242 op < Operation::EndUnaryOperationsSingleOutput && 243 cpp::IsSame<InputType, OutputType>::Value && 244 cpp::IsFloatingPointType<InputType>::Value) || 245 (Operation::BeginUnaryOperationsTwoOutputs < op && 246 op < Operation::EndUnaryOperationsTwoOutputs && 247 cpp::IsFloatingPointType<InputType>::Value && 248 cpp::IsSame<OutputType, BinaryOutput<InputType>>::Value) || 249 (Operation::BeginBinaryOperationsSingleOutput < op && 250 op < Operation::EndBinaryOperationsSingleOutput && 251 cpp::IsFloatingPointType<OutputType>::Value && 252 cpp::IsSame<InputType, BinaryInput<OutputType>>::Value) || 253 (Operation::BeginBinaryOperationsTwoOutputs < op && 254 op < Operation::EndBinaryOperationsTwoOutputs && 255 internal::AreMatchingBinaryInputAndBinaryOutput<InputType, 256 OutputType>::VALUE) || 257 (Operation::BeginTernaryOperationsSingleOuput < op && 258 op < Operation::EndTernaryOperationsSingleOutput && 259 cpp::IsFloatingPointType<OutputType>::Value && 260 cpp::IsSame<InputType, TernaryInput<OutputType>>::Value); 261 } 262 263 template <Operation op, typename InputType, typename OutputType> 264 __attribute__((no_sanitize("address"))) 265 cpp::EnableIfType<is_valid_operation<op, InputType, OutputType>(), 266 internal::MPFRMatcher<op, InputType, OutputType>> 267 get_mpfr_matcher(InputType input, OutputType output_unused, double t) { 268 return internal::MPFRMatcher<op, InputType, OutputType>(input, t); 269 } 270 271 enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest }; 272 273 template <typename T> T round(T x, RoundingMode mode); 274 275 template <typename T> bool round_to_long(T x, long &result); 276 template <typename T> bool round_to_long(T x, RoundingMode mode, long &result); 277 278 } // namespace mpfr 279 } // namespace testing 280 } // namespace __llvm_libc 281 282 #define EXPECT_MPFR_MATCH(op, input, match_value, tolerance) \ 283 EXPECT_THAT(match_value, __llvm_libc::testing::mpfr::get_mpfr_matcher<op>( \ 284 input, match_value, tolerance)) 285 286 #define ASSERT_MPFR_MATCH(op, input, match_value, tolerance) \ 287 ASSERT_THAT(match_value, __llvm_libc::testing::mpfr::get_mpfr_matcher<op>( \ 288 input, match_value, tolerance)) 289 290 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 291