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 "utils/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 Floor, 32 Round, 33 Sin, 34 Sqrt, 35 Tan, 36 Trunc, 37 EndUnaryOperationsSingleOutput, 38 39 // Operations which take a single floating point nubmer as input 40 // but produce two outputs. The first ouput is a floating point 41 // number of the same type as the input. The second output is of type 42 // 'int'. 43 BeginUnaryOperationsTwoOutputs, 44 Frexp, // Floating point output, the first output, is the fractional part. 45 EndUnaryOperationsTwoOutputs, 46 47 // Operations wich take two floating point nubmers of the same type as 48 // input and produce a single floating point number of the same type as 49 // output. 50 BeginBinaryOperationsSingleOutput, 51 Hypot, 52 EndBinaryOperationsSingleOutput, 53 54 // Operations which take two floating point numbers of the same type as 55 // input and produce two outputs. The first output is a floating nubmer of 56 // the same type as the inputs. The second output is af type 'int'. 57 BeginBinaryOperationsTwoOutputs, 58 RemQuo, // The first output, the floating point output, is the remainder. 59 EndBinaryOperationsTwoOutputs, 60 61 // Operations which take three floating point nubmers of the same type as 62 // input and produce a single floating point number of the same type as 63 // output. 64 BeginTernaryOperationsSingleOuput, 65 Fma, 66 EndTernaryOperationsSingleOutput, 67 }; 68 69 template <typename T> struct BinaryInput { 70 static_assert( 71 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 72 "Template parameter of BinaryInput must be a floating point type."); 73 74 using Type = T; 75 T x, y; 76 }; 77 78 template <typename T> struct TernaryInput { 79 static_assert( 80 __llvm_libc::cpp::IsFloatingPointType<T>::Value, 81 "Template parameter of TernaryInput must be a floating point type."); 82 83 using Type = T; 84 T x, y, z; 85 }; 86 87 template <typename T> struct BinaryOutput { 88 T f; 89 int i; 90 }; 91 92 namespace internal { 93 94 template <typename T1, typename T2> 95 struct AreMatchingBinaryInputAndBinaryOutput { 96 static constexpr bool value = false; 97 }; 98 99 template <typename T> 100 struct AreMatchingBinaryInputAndBinaryOutput<BinaryInput<T>, BinaryOutput<T>> { 101 static constexpr bool value = cpp::IsFloatingPointType<T>::Value; 102 }; 103 104 template <typename T> 105 bool compareUnaryOperationSingleOutput(Operation op, T input, T libcOutput, 106 double t); 107 template <typename T> 108 bool compareUnaryOperationTwoOutputs(Operation op, T input, 109 const BinaryOutput<T> &libcOutput, 110 double t); 111 template <typename T> 112 bool compareBinaryOperationTwoOutputs(Operation op, const BinaryInput<T> &input, 113 const BinaryOutput<T> &libcOutput, 114 double t); 115 116 template <typename T> 117 bool compareBinaryOperationOneOutput(Operation op, const BinaryInput<T> &input, 118 T libcOutput, double t); 119 120 template <typename T> 121 bool compareTernaryOperationOneOutput(Operation op, 122 const TernaryInput<T> &input, 123 T libcOutput, double t); 124 125 template <typename T> 126 void explainUnaryOperationSingleOutputError(Operation op, T input, T matchValue, 127 testutils::StreamWrapper &OS); 128 template <typename T> 129 void explainUnaryOperationTwoOutputsError(Operation op, T input, 130 const BinaryOutput<T> &matchValue, 131 testutils::StreamWrapper &OS); 132 template <typename T> 133 void explainBinaryOperationTwoOutputsError(Operation op, 134 const BinaryInput<T> &input, 135 const BinaryOutput<T> &matchValue, 136 testutils::StreamWrapper &OS); 137 138 template <typename T> 139 void explainBinaryOperationOneOutputError(Operation op, 140 const BinaryInput<T> &input, 141 T matchValue, 142 testutils::StreamWrapper &OS); 143 144 template <typename T> 145 void explainTernaryOperationOneOutputError(Operation op, 146 const TernaryInput<T> &input, 147 T matchValue, 148 testutils::StreamWrapper &OS); 149 150 template <Operation op, typename InputType, typename OutputType> 151 class MPFRMatcher : public testing::Matcher<OutputType> { 152 InputType input; 153 OutputType matchValue; 154 double ulpTolerance; 155 156 public: 157 MPFRMatcher(InputType testInput, double ulpTolerance) 158 : input(testInput), ulpTolerance(ulpTolerance) {} 159 160 bool match(OutputType libcResult) { 161 matchValue = libcResult; 162 return match(input, matchValue, ulpTolerance); 163 } 164 165 void explainError(testutils::StreamWrapper &OS) override { 166 explainError(input, matchValue, OS); 167 } 168 169 private: 170 template <typename T> static bool match(T in, T out, double tolerance) { 171 return compareUnaryOperationSingleOutput(op, in, out, tolerance); 172 } 173 174 template <typename T> 175 static bool match(T in, const BinaryOutput<T> &out, double tolerance) { 176 return compareUnaryOperationTwoOutputs(op, in, out, tolerance); 177 } 178 179 template <typename T> 180 static bool match(const BinaryInput<T> &in, T out, double tolerance) { 181 return compareBinaryOperationOneOutput(op, in, out, tolerance); 182 } 183 184 template <typename T> 185 static bool match(BinaryInput<T> in, const BinaryOutput<T> &out, 186 double tolerance) { 187 return compareBinaryOperationTwoOutputs(op, in, out, tolerance); 188 } 189 190 template <typename T> 191 static bool match(const TernaryInput<T> &in, T out, double tolerance) { 192 return compareTernaryOperationOneOutput(op, in, out, tolerance); 193 } 194 195 template <typename T> 196 static void explainError(T in, T out, testutils::StreamWrapper &OS) { 197 explainUnaryOperationSingleOutputError(op, in, out, OS); 198 } 199 200 template <typename T> 201 static void explainError(T in, const BinaryOutput<T> &out, 202 testutils::StreamWrapper &OS) { 203 explainUnaryOperationTwoOutputsError(op, in, out, OS); 204 } 205 206 template <typename T> 207 static void explainError(const BinaryInput<T> &in, const BinaryOutput<T> &out, 208 testutils::StreamWrapper &OS) { 209 explainBinaryOperationTwoOutputsError(op, in, out, OS); 210 } 211 212 template <typename T> 213 static void explainError(const BinaryInput<T> &in, T out, 214 testutils::StreamWrapper &OS) { 215 explainBinaryOperationOneOutputError(op, in, out, OS); 216 } 217 218 template <typename T> 219 static void explainError(const TernaryInput<T> &in, T out, 220 testutils::StreamWrapper &OS) { 221 explainTernaryOperationOneOutputError(op, in, out, OS); 222 } 223 }; 224 225 } // namespace internal 226 227 // Return true if the input and ouput types for the operation op are valid 228 // types. 229 template <Operation op, typename InputType, typename OutputType> 230 constexpr bool isValidOperation() { 231 return (Operation::BeginUnaryOperationsSingleOutput < op && 232 op < Operation::EndUnaryOperationsSingleOutput && 233 cpp::IsSame<InputType, OutputType>::Value && 234 cpp::IsFloatingPointType<InputType>::Value) || 235 (Operation::BeginUnaryOperationsTwoOutputs < op && 236 op < Operation::EndUnaryOperationsTwoOutputs && 237 cpp::IsFloatingPointType<InputType>::Value && 238 cpp::IsSame<OutputType, BinaryOutput<InputType>>::Value) || 239 (Operation::BeginBinaryOperationsSingleOutput < op && 240 op < Operation::EndBinaryOperationsSingleOutput && 241 cpp::IsFloatingPointType<OutputType>::Value && 242 cpp::IsSame<InputType, BinaryInput<OutputType>>::Value) || 243 (Operation::BeginBinaryOperationsTwoOutputs < op && 244 op < Operation::EndBinaryOperationsTwoOutputs && 245 internal::AreMatchingBinaryInputAndBinaryOutput<InputType, 246 OutputType>::value) || 247 (Operation::BeginTernaryOperationsSingleOuput < op && 248 op < Operation::EndTernaryOperationsSingleOutput && 249 cpp::IsFloatingPointType<OutputType>::Value && 250 cpp::IsSame<InputType, TernaryInput<OutputType>>::Value); 251 } 252 253 template <Operation op, typename InputType, typename OutputType> 254 __attribute__((no_sanitize("address"))) 255 cpp::EnableIfType<isValidOperation<op, InputType, OutputType>(), 256 internal::MPFRMatcher<op, InputType, OutputType>> 257 getMPFRMatcher(InputType input, OutputType outputUnused, double t) { 258 return internal::MPFRMatcher<op, InputType, OutputType>(input, t); 259 } 260 261 enum class RoundingMode : uint8_t { Upward, Downward, TowardZero, Nearest }; 262 263 template <typename T> T Round(T x, RoundingMode mode); 264 265 template <typename T> bool RoundToLong(T x, long &result); 266 template <typename T> bool RoundToLong(T x, RoundingMode mode, long &result); 267 268 } // namespace mpfr 269 } // namespace testing 270 } // namespace __llvm_libc 271 272 #define EXPECT_MPFR_MATCH(op, input, matchValue, tolerance) \ 273 EXPECT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>( \ 274 input, matchValue, tolerance)) 275 276 #define ASSERT_MPFR_MATCH(op, input, matchValue, tolerance) \ 277 ASSERT_THAT(matchValue, __llvm_libc::testing::mpfr::getMPFRMatcher<op>( \ 278 input, matchValue, tolerance)) 279 280 #endif // LLVM_LIBC_UTILS_TESTUTILS_MPFRUTILS_H 281