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