| 1 | //===-- Utils which wrap MPFR ---------------------------------------------===// |
| 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 | #include "MPFRUtils.h" |
| 10 | #include "MPCommon.h" |
| 11 | |
| 12 | #include "src/__support/CPP/array.h" |
| 13 | #include "src/__support/CPP/stringstream.h" |
| 14 | #include "src/__support/FPUtil/fpbits_str.h" |
| 15 | #include "src/__support/macros/config.h" |
| 16 | #include "src/__support/macros/properties/types.h" |
| 17 | |
| 18 | namespace LIBC_NAMESPACE_DECL { |
| 19 | namespace testing { |
| 20 | namespace mpfr { |
| 21 | namespace internal { |
| 22 | |
| 23 | template <typename InputType> |
| 24 | cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber> |
| 25 | unary_operation(Operation op, InputType input, unsigned int precision, |
| 26 | RoundingMode rounding) { |
| 27 | MPFRNumber mpfrInput(input, precision, rounding); |
| 28 | switch (op) { |
| 29 | case Operation::Abs: |
| 30 | return mpfrInput.abs(); |
| 31 | case Operation::Acos: |
| 32 | return mpfrInput.acos(); |
| 33 | case Operation::Acosh: |
| 34 | return mpfrInput.acosh(); |
| 35 | case Operation::Acospi: |
| 36 | return mpfrInput.acospi(); |
| 37 | case Operation::Asin: |
| 38 | return mpfrInput.asin(); |
| 39 | case Operation::Asinh: |
| 40 | return mpfrInput.asinh(); |
| 41 | case Operation::Atan: |
| 42 | return mpfrInput.atan(); |
| 43 | case Operation::Atanh: |
| 44 | return mpfrInput.atanh(); |
| 45 | case Operation::Cbrt: |
| 46 | return mpfrInput.cbrt(); |
| 47 | case Operation::Ceil: |
| 48 | return mpfrInput.ceil(); |
| 49 | case Operation::Cos: |
| 50 | return mpfrInput.cos(); |
| 51 | case Operation::Cosh: |
| 52 | return mpfrInput.cosh(); |
| 53 | case Operation::Cospi: |
| 54 | return mpfrInput.cospi(); |
| 55 | case Operation::Erf: |
| 56 | return mpfrInput.erf(); |
| 57 | case Operation::Exp: |
| 58 | return mpfrInput.exp(); |
| 59 | case Operation::Exp2: |
| 60 | return mpfrInput.exp2(); |
| 61 | case Operation::Exp2m1: |
| 62 | return mpfrInput.exp2m1(); |
| 63 | case Operation::Exp10: |
| 64 | return mpfrInput.exp10(); |
| 65 | case Operation::Exp10m1: |
| 66 | return mpfrInput.exp10m1(); |
| 67 | case Operation::Expm1: |
| 68 | return mpfrInput.expm1(); |
| 69 | case Operation::Floor: |
| 70 | return mpfrInput.floor(); |
| 71 | case Operation::Log: |
| 72 | return mpfrInput.log(); |
| 73 | case Operation::Log2: |
| 74 | return mpfrInput.log2(); |
| 75 | case Operation::Log10: |
| 76 | return mpfrInput.log10(); |
| 77 | case Operation::Log1p: |
| 78 | return mpfrInput.log1p(); |
| 79 | case Operation::Mod2PI: |
| 80 | return mpfrInput.mod_2pi(); |
| 81 | case Operation::ModPIOver2: |
| 82 | return mpfrInput.mod_pi_over_2(); |
| 83 | case Operation::ModPIOver4: |
| 84 | return mpfrInput.mod_pi_over_4(); |
| 85 | case Operation::Round: |
| 86 | return mpfrInput.round(); |
| 87 | case Operation::RoundEven: |
| 88 | return mpfrInput.roundeven(); |
| 89 | case Operation::Sin: |
| 90 | return mpfrInput.sin(); |
| 91 | case Operation::Sinpi: |
| 92 | return mpfrInput.sinpi(); |
| 93 | case Operation::Sinh: |
| 94 | return mpfrInput.sinh(); |
| 95 | case Operation::Sqrt: |
| 96 | return mpfrInput.sqrt(); |
| 97 | case Operation::Tan: |
| 98 | return mpfrInput.tan(); |
| 99 | case Operation::Tanh: |
| 100 | return mpfrInput.tanh(); |
| 101 | case Operation::Tanpi: |
| 102 | return mpfrInput.tanpi(); |
| 103 | case Operation::Trunc: |
| 104 | return mpfrInput.trunc(); |
| 105 | default: |
| 106 | __builtin_unreachable(); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | template <typename InputType> |
| 111 | cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber> |
| 112 | unary_operation_two_outputs(Operation op, InputType input, int &output, |
| 113 | unsigned int precision, RoundingMode rounding) { |
| 114 | MPFRNumber mpfrInput(input, precision, rounding); |
| 115 | switch (op) { |
| 116 | case Operation::Frexp: |
| 117 | return mpfrInput.frexp(output); |
| 118 | default: |
| 119 | __builtin_unreachable(); |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | template <typename InputType> |
| 124 | cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber> |
| 125 | binary_operation_one_output(Operation op, InputType x, InputType y, |
| 126 | unsigned int precision, RoundingMode rounding) { |
| 127 | MPFRNumber inputX(x, precision, rounding); |
| 128 | MPFRNumber inputY(y, precision, rounding); |
| 129 | switch (op) { |
| 130 | case Operation::Add: |
| 131 | return inputX.add(inputY); |
| 132 | case Operation::Atan2: |
| 133 | return inputX.atan2(inputY); |
| 134 | case Operation::Div: |
| 135 | return inputX.div(inputY); |
| 136 | case Operation::Fmod: |
| 137 | return inputX.fmod(inputY); |
| 138 | case Operation::Hypot: |
| 139 | return inputX.hypot(inputY); |
| 140 | case Operation::Mul: |
| 141 | return inputX.mul(inputY); |
| 142 | case Operation::Pow: |
| 143 | return inputX.pow(inputY); |
| 144 | case Operation::Sub: |
| 145 | return inputX.sub(inputY); |
| 146 | default: |
| 147 | __builtin_unreachable(); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | template <typename InputType> |
| 152 | cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber> |
| 153 | binary_operation_two_outputs(Operation op, InputType x, InputType y, |
| 154 | int &output, unsigned int precision, |
| 155 | RoundingMode rounding) { |
| 156 | MPFRNumber inputX(x, precision, rounding); |
| 157 | MPFRNumber inputY(y, precision, rounding); |
| 158 | switch (op) { |
| 159 | case Operation::RemQuo: |
| 160 | return inputX.remquo(inputY, output); |
| 161 | default: |
| 162 | __builtin_unreachable(); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | template <typename InputType> |
| 167 | cpp::enable_if_t<cpp::is_floating_point_v<InputType>, MPFRNumber> |
| 168 | ternary_operation_one_output(Operation op, InputType x, InputType y, |
| 169 | InputType z, unsigned int precision, |
| 170 | RoundingMode rounding) { |
| 171 | // For FMA function, we just need to compare with the mpfr_fma with the same |
| 172 | // precision as InputType. Using higher precision as the intermediate results |
| 173 | // to compare might incorrectly fail due to double-rounding errors. |
| 174 | MPFRNumber inputX(x, precision, rounding); |
| 175 | MPFRNumber inputY(y, precision, rounding); |
| 176 | MPFRNumber inputZ(z, precision, rounding); |
| 177 | switch (op) { |
| 178 | case Operation::Fma: |
| 179 | return inputX.fma(inputY, inputZ); |
| 180 | default: |
| 181 | __builtin_unreachable(); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | // Remark: For all the explain_*_error functions, we will use std::stringstream |
| 186 | // to build the complete error messages before sending it to the outstream `OS` |
| 187 | // once at the end. This will stop the error messages from interleaving when |
| 188 | // the tests are running concurrently. |
| 189 | template <typename InputType, typename OutputType> |
| 190 | void explain_unary_operation_single_output_error(Operation op, InputType input, |
| 191 | OutputType matchValue, |
| 192 | double ulp_tolerance, |
| 193 | RoundingMode rounding) { |
| 194 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 195 | MPFRNumber mpfrInput(input, precision); |
| 196 | MPFRNumber mpfr_result; |
| 197 | mpfr_result = unary_operation(op, input, precision, rounding); |
| 198 | MPFRNumber mpfrMatchValue(matchValue); |
| 199 | cpp::array<char, 1024> msg_buf; |
| 200 | cpp::StringStream msg(msg_buf); |
| 201 | msg << "Match value not within tolerance value of MPFR result:\n" |
| 202 | << " Input decimal: " << mpfrInput.str() << '\n'; |
| 203 | msg << " Input bits: " << str(FPBits<InputType>(input)) << '\n'; |
| 204 | msg << '\n' << " Match decimal: " << mpfrMatchValue.str() << '\n'; |
| 205 | msg << " Match bits: " << str(FPBits<OutputType>(matchValue)) << '\n'; |
| 206 | msg << '\n' << " MPFR result: " << mpfr_result.str() << '\n'; |
| 207 | msg << " MPFR rounded: " |
| 208 | << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n'; |
| 209 | msg << '\n'; |
| 210 | msg << " ULP error: " << mpfr_result.ulp_as_mpfr_number(matchValue).str() |
| 211 | << '\n'; |
| 212 | if (msg.overflow()) |
| 213 | __builtin_unreachable(); |
| 214 | tlog << msg.str(); |
| 215 | } |
| 216 | |
| 217 | template void explain_unary_operation_single_output_error(Operation op, float, |
| 218 | float, double, |
| 219 | RoundingMode); |
| 220 | template void explain_unary_operation_single_output_error(Operation op, double, |
| 221 | double, double, |
| 222 | RoundingMode); |
| 223 | template void explain_unary_operation_single_output_error(Operation op, |
| 224 | long double, |
| 225 | long double, double, |
| 226 | RoundingMode); |
| 227 | template void explain_unary_operation_single_output_error(Operation op, double, |
| 228 | float, double, |
| 229 | RoundingMode); |
| 230 | template void explain_unary_operation_single_output_error(Operation op, |
| 231 | long double, float, |
| 232 | double, RoundingMode); |
| 233 | template void explain_unary_operation_single_output_error(Operation op, |
| 234 | long double, double, |
| 235 | double, RoundingMode); |
| 236 | |
| 237 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 238 | template void explain_unary_operation_single_output_error(Operation op, float16, |
| 239 | float16, double, |
| 240 | RoundingMode); |
| 241 | template void explain_unary_operation_single_output_error(Operation op, float, |
| 242 | float16, double, |
| 243 | RoundingMode); |
| 244 | template void explain_unary_operation_single_output_error(Operation op, double, |
| 245 | float16, double, |
| 246 | RoundingMode); |
| 247 | template void explain_unary_operation_single_output_error(Operation op, |
| 248 | long double, float16, |
| 249 | double, RoundingMode); |
| 250 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 251 | template void explain_unary_operation_single_output_error(Operation op, |
| 252 | float128, float16, |
| 253 | double, RoundingMode); |
| 254 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 255 | #endif // LIBC_TYPES_HAS_FLOAT16 |
| 256 | |
| 257 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 258 | template void explain_unary_operation_single_output_error(Operation op, |
| 259 | float128, float128, |
| 260 | double, RoundingMode); |
| 261 | template void explain_unary_operation_single_output_error(Operation op, |
| 262 | float128, float, |
| 263 | double, RoundingMode); |
| 264 | template void explain_unary_operation_single_output_error(Operation op, |
| 265 | float128, double, |
| 266 | double, RoundingMode); |
| 267 | template void explain_unary_operation_single_output_error(Operation op, |
| 268 | float128, long double, |
| 269 | double, RoundingMode); |
| 270 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 271 | |
| 272 | template <typename T> |
| 273 | void explain_unary_operation_two_outputs_error( |
| 274 | Operation op, T input, const BinaryOutput<T> &libc_result, |
| 275 | double ulp_tolerance, RoundingMode rounding) { |
| 276 | unsigned int precision = get_precision<T>(ulp_tolerance); |
| 277 | MPFRNumber mpfrInput(input, precision); |
| 278 | int mpfrIntResult; |
| 279 | MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult, |
| 280 | precision, rounding); |
| 281 | |
| 282 | if (mpfrIntResult != libc_result.i) { |
| 283 | tlog << "MPFR integral result: " << mpfrIntResult << '\n' |
| 284 | << "Libc integral result: " << libc_result.i << '\n'; |
| 285 | } else { |
| 286 | tlog << "Integral result from libc matches integral result from MPFR.\n" ; |
| 287 | } |
| 288 | |
| 289 | MPFRNumber mpfrMatchValue(libc_result.f); |
| 290 | tlog |
| 291 | << "Libc floating point result is not within tolerance value of the MPFR " |
| 292 | << "result.\n\n" ; |
| 293 | |
| 294 | tlog << " Input decimal: " << mpfrInput.str() << "\n\n" ; |
| 295 | |
| 296 | tlog << "Libc floating point value: " << mpfrMatchValue.str() << '\n'; |
| 297 | tlog << " Libc floating point bits: " << str(FPBits<T>(libc_result.f)) |
| 298 | << '\n'; |
| 299 | tlog << "\n\n" ; |
| 300 | |
| 301 | tlog << " MPFR result: " << mpfr_result.str() << '\n'; |
| 302 | tlog << " MPFR rounded: " << str(FPBits<T>(mpfr_result.as<T>())) |
| 303 | << '\n'; |
| 304 | tlog << '\n' |
| 305 | << " ULP error: " |
| 306 | << mpfr_result.ulp_as_mpfr_number(libc_result.f).str() << '\n'; |
| 307 | } |
| 308 | |
| 309 | template void explain_unary_operation_two_outputs_error<float>( |
| 310 | Operation, float, const BinaryOutput<float> &, double, RoundingMode); |
| 311 | template void explain_unary_operation_two_outputs_error<double>( |
| 312 | Operation, double, const BinaryOutput<double> &, double, RoundingMode); |
| 313 | template void explain_unary_operation_two_outputs_error<long double>( |
| 314 | Operation, long double, const BinaryOutput<long double> &, double, |
| 315 | RoundingMode); |
| 316 | |
| 317 | template <typename T> |
| 318 | void explain_binary_operation_two_outputs_error( |
| 319 | Operation op, const BinaryInput<T> &input, |
| 320 | const BinaryOutput<T> &libc_result, double ulp_tolerance, |
| 321 | RoundingMode rounding) { |
| 322 | unsigned int precision = get_precision<T>(ulp_tolerance); |
| 323 | MPFRNumber mpfrX(input.x, precision); |
| 324 | MPFRNumber mpfrY(input.y, precision); |
| 325 | int mpfrIntResult; |
| 326 | MPFRNumber mpfr_result = binary_operation_two_outputs( |
| 327 | op, input.x, input.y, mpfrIntResult, precision, rounding); |
| 328 | MPFRNumber mpfrMatchValue(libc_result.f); |
| 329 | |
| 330 | tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n' |
| 331 | << "MPFR integral result: " << mpfrIntResult << '\n' |
| 332 | << "Libc integral result: " << libc_result.i << '\n' |
| 333 | << "Libc floating point result: " << mpfrMatchValue.str() << '\n' |
| 334 | << " MPFR result: " << mpfr_result.str() << '\n'; |
| 335 | tlog << "Libc floating point result bits: " << str(FPBits<T>(libc_result.f)) |
| 336 | << '\n'; |
| 337 | tlog << " MPFR rounded bits: " |
| 338 | << str(FPBits<T>(mpfr_result.as<T>())) << '\n'; |
| 339 | tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result.f).str() |
| 340 | << '\n'; |
| 341 | } |
| 342 | |
| 343 | template void explain_binary_operation_two_outputs_error<float>( |
| 344 | Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double, |
| 345 | RoundingMode); |
| 346 | template void explain_binary_operation_two_outputs_error<double>( |
| 347 | Operation, const BinaryInput<double> &, const BinaryOutput<double> &, |
| 348 | double, RoundingMode); |
| 349 | template void explain_binary_operation_two_outputs_error<long double>( |
| 350 | Operation, const BinaryInput<long double> &, |
| 351 | const BinaryOutput<long double> &, double, RoundingMode); |
| 352 | |
| 353 | template <typename InputType, typename OutputType> |
| 354 | void explain_binary_operation_one_output_error( |
| 355 | Operation op, const BinaryInput<InputType> &input, OutputType libc_result, |
| 356 | double ulp_tolerance, RoundingMode rounding) { |
| 357 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 358 | MPFRNumber mpfrX(input.x, precision); |
| 359 | MPFRNumber mpfrY(input.y, precision); |
| 360 | FPBits<InputType> xbits(input.x); |
| 361 | FPBits<InputType> ybits(input.y); |
| 362 | MPFRNumber mpfr_result = |
| 363 | binary_operation_one_output(op, input.x, input.y, precision, rounding); |
| 364 | MPFRNumber mpfrMatchValue(libc_result); |
| 365 | |
| 366 | tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() << '\n'; |
| 367 | tlog << "First input bits: " << str(FPBits<InputType>(input.x)) << '\n'; |
| 368 | tlog << "Second input bits: " << str(FPBits<InputType>(input.y)) << '\n'; |
| 369 | |
| 370 | tlog << "Libc result: " << mpfrMatchValue.str() << '\n' |
| 371 | << "MPFR result: " << mpfr_result.str() << '\n'; |
| 372 | tlog << "Libc floating point result bits: " |
| 373 | << str(FPBits<OutputType>(libc_result)) << '\n'; |
| 374 | tlog << " MPFR rounded bits: " |
| 375 | << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n'; |
| 376 | tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result).str() |
| 377 | << '\n'; |
| 378 | } |
| 379 | |
| 380 | template void |
| 381 | explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &, |
| 382 | float, double, RoundingMode); |
| 383 | template void explain_binary_operation_one_output_error( |
| 384 | Operation, const BinaryInput<double> &, float, double, RoundingMode); |
| 385 | template void explain_binary_operation_one_output_error( |
| 386 | Operation, const BinaryInput<double> &, double, double, RoundingMode); |
| 387 | template void explain_binary_operation_one_output_error( |
| 388 | Operation, const BinaryInput<long double> &, float, double, RoundingMode); |
| 389 | template void explain_binary_operation_one_output_error( |
| 390 | Operation, const BinaryInput<long double> &, double, double, RoundingMode); |
| 391 | template void |
| 392 | explain_binary_operation_one_output_error(Operation, |
| 393 | const BinaryInput<long double> &, |
| 394 | long double, double, RoundingMode); |
| 395 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 396 | template void explain_binary_operation_one_output_error( |
| 397 | Operation, const BinaryInput<float16> &, float16, double, RoundingMode); |
| 398 | template void |
| 399 | explain_binary_operation_one_output_error(Operation, const BinaryInput<float> &, |
| 400 | float16, double, RoundingMode); |
| 401 | template void explain_binary_operation_one_output_error( |
| 402 | Operation, const BinaryInput<double> &, float16, double, RoundingMode); |
| 403 | template void explain_binary_operation_one_output_error( |
| 404 | Operation, const BinaryInput<long double> &, float16, double, RoundingMode); |
| 405 | #endif |
| 406 | #if defined(LIBC_TYPES_HAS_FLOAT128) && \ |
| 407 | defined(LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE) |
| 408 | template void explain_binary_operation_one_output_error( |
| 409 | Operation, const BinaryInput<float128> &, float128, double, RoundingMode); |
| 410 | #endif |
| 411 | |
| 412 | template <typename InputType, typename OutputType> |
| 413 | void explain_ternary_operation_one_output_error( |
| 414 | Operation op, const TernaryInput<InputType> &input, OutputType libc_result, |
| 415 | double ulp_tolerance, RoundingMode rounding) { |
| 416 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 417 | MPFRNumber mpfrX(input.x, precision); |
| 418 | MPFRNumber mpfrY(input.y, precision); |
| 419 | MPFRNumber mpfrZ(input.z, precision); |
| 420 | FPBits<InputType> xbits(input.x); |
| 421 | FPBits<InputType> ybits(input.y); |
| 422 | FPBits<InputType> zbits(input.z); |
| 423 | MPFRNumber mpfr_result = ternary_operation_one_output( |
| 424 | op, input.x, input.y, input.z, precision, rounding); |
| 425 | MPFRNumber mpfrMatchValue(libc_result); |
| 426 | |
| 427 | tlog << "Input decimal: x: " << mpfrX.str() << " y: " << mpfrY.str() |
| 428 | << " z: " << mpfrZ.str() << '\n'; |
| 429 | tlog << " First input bits: " << str(FPBits<InputType>(input.x)) << '\n'; |
| 430 | tlog << "Second input bits: " << str(FPBits<InputType>(input.y)) << '\n'; |
| 431 | tlog << " Third input bits: " << str(FPBits<InputType>(input.z)) << '\n'; |
| 432 | |
| 433 | tlog << "Libc result: " << mpfrMatchValue.str() << '\n' |
| 434 | << "MPFR result: " << mpfr_result.str() << '\n'; |
| 435 | tlog << "Libc floating point result bits: " |
| 436 | << str(FPBits<OutputType>(libc_result)) << '\n'; |
| 437 | tlog << " MPFR rounded bits: " |
| 438 | << str(FPBits<OutputType>(mpfr_result.as<OutputType>())) << '\n'; |
| 439 | tlog << "ULP error: " << mpfr_result.ulp_as_mpfr_number(libc_result).str() |
| 440 | << '\n'; |
| 441 | } |
| 442 | |
| 443 | template void explain_ternary_operation_one_output_error( |
| 444 | Operation, const TernaryInput<float> &, float, double, RoundingMode); |
| 445 | template void explain_ternary_operation_one_output_error( |
| 446 | Operation, const TernaryInput<double> &, float, double, RoundingMode); |
| 447 | template void explain_ternary_operation_one_output_error( |
| 448 | Operation, const TernaryInput<double> &, double, double, RoundingMode); |
| 449 | template void explain_ternary_operation_one_output_error( |
| 450 | Operation, const TernaryInput<long double> &, float, double, RoundingMode); |
| 451 | template void explain_ternary_operation_one_output_error( |
| 452 | Operation, const TernaryInput<long double> &, double, double, RoundingMode); |
| 453 | template void |
| 454 | explain_ternary_operation_one_output_error(Operation, |
| 455 | const TernaryInput<long double> &, |
| 456 | long double, double, RoundingMode); |
| 457 | |
| 458 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 459 | template void explain_ternary_operation_one_output_error( |
| 460 | Operation, const TernaryInput<float16> &, float16, double, RoundingMode); |
| 461 | template void explain_ternary_operation_one_output_error( |
| 462 | Operation, const TernaryInput<float> &, float16, double, RoundingMode); |
| 463 | template void explain_ternary_operation_one_output_error( |
| 464 | Operation, const TernaryInput<double> &, float16, double, RoundingMode); |
| 465 | template void |
| 466 | explain_ternary_operation_one_output_error(Operation, |
| 467 | const TernaryInput<long double> &, |
| 468 | float16, double, RoundingMode); |
| 469 | #endif |
| 470 | |
| 471 | template <typename InputType, typename OutputType> |
| 472 | bool compare_unary_operation_single_output(Operation op, InputType input, |
| 473 | OutputType libc_result, |
| 474 | double ulp_tolerance, |
| 475 | RoundingMode rounding) { |
| 476 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 477 | MPFRNumber mpfr_result; |
| 478 | mpfr_result = unary_operation(op, input, precision, rounding); |
| 479 | double ulp = mpfr_result.ulp(libc_result); |
| 480 | return (ulp <= ulp_tolerance); |
| 481 | } |
| 482 | |
| 483 | template bool compare_unary_operation_single_output(Operation, float, float, |
| 484 | double, RoundingMode); |
| 485 | template bool compare_unary_operation_single_output(Operation, double, double, |
| 486 | double, RoundingMode); |
| 487 | template bool compare_unary_operation_single_output(Operation, long double, |
| 488 | long double, double, |
| 489 | RoundingMode); |
| 490 | template bool compare_unary_operation_single_output(Operation, double, float, |
| 491 | double, RoundingMode); |
| 492 | template bool compare_unary_operation_single_output(Operation, long double, |
| 493 | float, double, |
| 494 | RoundingMode); |
| 495 | template bool compare_unary_operation_single_output(Operation, long double, |
| 496 | double, double, |
| 497 | RoundingMode); |
| 498 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 499 | template bool compare_unary_operation_single_output(Operation, float16, float16, |
| 500 | double, RoundingMode); |
| 501 | template bool compare_unary_operation_single_output(Operation, float, float16, |
| 502 | double, RoundingMode); |
| 503 | template bool compare_unary_operation_single_output(Operation, double, float16, |
| 504 | double, RoundingMode); |
| 505 | template bool compare_unary_operation_single_output(Operation, long double, |
| 506 | float16, double, |
| 507 | RoundingMode); |
| 508 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 509 | template bool compare_unary_operation_single_output(Operation, float128, |
| 510 | float16, double, |
| 511 | RoundingMode); |
| 512 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 513 | #endif // LIBC_TYPES_HAS_FLOAT16 |
| 514 | |
| 515 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 516 | template bool compare_unary_operation_single_output(Operation, float128, |
| 517 | float128, double, |
| 518 | RoundingMode); |
| 519 | template bool compare_unary_operation_single_output(Operation, float128, float, |
| 520 | double, RoundingMode); |
| 521 | template bool compare_unary_operation_single_output(Operation, float128, double, |
| 522 | double, RoundingMode); |
| 523 | template bool compare_unary_operation_single_output(Operation, float128, |
| 524 | long double, double, |
| 525 | RoundingMode); |
| 526 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 527 | |
| 528 | template <typename T> |
| 529 | bool compare_unary_operation_two_outputs(Operation op, T input, |
| 530 | const BinaryOutput<T> &libc_result, |
| 531 | double ulp_tolerance, |
| 532 | RoundingMode rounding) { |
| 533 | int mpfrIntResult; |
| 534 | unsigned int precision = get_precision<T>(ulp_tolerance); |
| 535 | MPFRNumber mpfr_result = unary_operation_two_outputs(op, input, mpfrIntResult, |
| 536 | precision, rounding); |
| 537 | double ulp = mpfr_result.ulp(libc_result.f); |
| 538 | |
| 539 | if (mpfrIntResult != libc_result.i) |
| 540 | return false; |
| 541 | |
| 542 | return (ulp <= ulp_tolerance); |
| 543 | } |
| 544 | |
| 545 | template bool compare_unary_operation_two_outputs<float>( |
| 546 | Operation, float, const BinaryOutput<float> &, double, RoundingMode); |
| 547 | template bool compare_unary_operation_two_outputs<double>( |
| 548 | Operation, double, const BinaryOutput<double> &, double, RoundingMode); |
| 549 | template bool compare_unary_operation_two_outputs<long double>( |
| 550 | Operation, long double, const BinaryOutput<long double> &, double, |
| 551 | RoundingMode); |
| 552 | |
| 553 | template <typename T> |
| 554 | bool compare_binary_operation_two_outputs(Operation op, |
| 555 | const BinaryInput<T> &input, |
| 556 | const BinaryOutput<T> &libc_result, |
| 557 | double ulp_tolerance, |
| 558 | RoundingMode rounding) { |
| 559 | int mpfrIntResult; |
| 560 | unsigned int precision = get_precision<T>(ulp_tolerance); |
| 561 | MPFRNumber mpfr_result = binary_operation_two_outputs( |
| 562 | op, input.x, input.y, mpfrIntResult, precision, rounding); |
| 563 | double ulp = mpfr_result.ulp(libc_result.f); |
| 564 | |
| 565 | if (mpfrIntResult != libc_result.i) { |
| 566 | if (op == Operation::RemQuo) { |
| 567 | if ((0x7 & mpfrIntResult) != (0x7 & libc_result.i)) |
| 568 | return false; |
| 569 | } else { |
| 570 | return false; |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | return (ulp <= ulp_tolerance); |
| 575 | } |
| 576 | |
| 577 | template bool compare_binary_operation_two_outputs<float>( |
| 578 | Operation, const BinaryInput<float> &, const BinaryOutput<float> &, double, |
| 579 | RoundingMode); |
| 580 | template bool compare_binary_operation_two_outputs<double>( |
| 581 | Operation, const BinaryInput<double> &, const BinaryOutput<double> &, |
| 582 | double, RoundingMode); |
| 583 | template bool compare_binary_operation_two_outputs<long double>( |
| 584 | Operation, const BinaryInput<long double> &, |
| 585 | const BinaryOutput<long double> &, double, RoundingMode); |
| 586 | |
| 587 | template <typename InputType, typename OutputType> |
| 588 | bool compare_binary_operation_one_output(Operation op, |
| 589 | const BinaryInput<InputType> &input, |
| 590 | OutputType libc_result, |
| 591 | double ulp_tolerance, |
| 592 | RoundingMode rounding) { |
| 593 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 594 | MPFRNumber mpfr_result = |
| 595 | binary_operation_one_output(op, input.x, input.y, precision, rounding); |
| 596 | double ulp = mpfr_result.ulp(libc_result); |
| 597 | |
| 598 | return (ulp <= ulp_tolerance); |
| 599 | } |
| 600 | |
| 601 | template bool compare_binary_operation_one_output(Operation, |
| 602 | const BinaryInput<float> &, |
| 603 | float, double, RoundingMode); |
| 604 | template bool compare_binary_operation_one_output(Operation, |
| 605 | const BinaryInput<double> &, |
| 606 | double, double, RoundingMode); |
| 607 | template bool |
| 608 | compare_binary_operation_one_output(Operation, const BinaryInput<long double> &, |
| 609 | float, double, RoundingMode); |
| 610 | template bool |
| 611 | compare_binary_operation_one_output(Operation, const BinaryInput<long double> &, |
| 612 | double, double, RoundingMode); |
| 613 | template bool |
| 614 | compare_binary_operation_one_output(Operation, const BinaryInput<long double> &, |
| 615 | long double, double, RoundingMode); |
| 616 | |
| 617 | template bool compare_binary_operation_one_output(Operation, |
| 618 | const BinaryInput<double> &, |
| 619 | float, double, RoundingMode); |
| 620 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 621 | template bool compare_binary_operation_one_output(Operation, |
| 622 | const BinaryInput<float16> &, |
| 623 | float16, double, |
| 624 | RoundingMode); |
| 625 | template bool compare_binary_operation_one_output(Operation, |
| 626 | const BinaryInput<float> &, |
| 627 | float16, double, |
| 628 | RoundingMode); |
| 629 | template bool compare_binary_operation_one_output(Operation, |
| 630 | const BinaryInput<double> &, |
| 631 | float16, double, |
| 632 | RoundingMode); |
| 633 | template bool |
| 634 | compare_binary_operation_one_output(Operation, const BinaryInput<long double> &, |
| 635 | float16, double, RoundingMode); |
| 636 | #endif |
| 637 | #if defined(LIBC_TYPES_HAS_FLOAT128) && \ |
| 638 | defined(LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE) |
| 639 | template bool compare_binary_operation_one_output(Operation, |
| 640 | const BinaryInput<float128> &, |
| 641 | float128, double, |
| 642 | RoundingMode); |
| 643 | #endif |
| 644 | |
| 645 | template <typename InputType, typename OutputType> |
| 646 | bool compare_ternary_operation_one_output(Operation op, |
| 647 | const TernaryInput<InputType> &input, |
| 648 | OutputType libc_result, |
| 649 | double ulp_tolerance, |
| 650 | RoundingMode rounding) { |
| 651 | unsigned int precision = get_precision<InputType>(ulp_tolerance); |
| 652 | MPFRNumber mpfr_result = ternary_operation_one_output( |
| 653 | op, input.x, input.y, input.z, precision, rounding); |
| 654 | double ulp = mpfr_result.ulp(libc_result); |
| 655 | |
| 656 | return (ulp <= ulp_tolerance); |
| 657 | } |
| 658 | |
| 659 | template bool compare_ternary_operation_one_output(Operation, |
| 660 | const TernaryInput<float> &, |
| 661 | float, double, RoundingMode); |
| 662 | template bool compare_ternary_operation_one_output(Operation, |
| 663 | const TernaryInput<double> &, |
| 664 | float, double, RoundingMode); |
| 665 | template bool compare_ternary_operation_one_output(Operation, |
| 666 | const TernaryInput<double> &, |
| 667 | double, double, |
| 668 | RoundingMode); |
| 669 | template bool compare_ternary_operation_one_output( |
| 670 | Operation, const TernaryInput<long double> &, float, double, RoundingMode); |
| 671 | template bool compare_ternary_operation_one_output( |
| 672 | Operation, const TernaryInput<long double> &, double, double, RoundingMode); |
| 673 | template bool |
| 674 | compare_ternary_operation_one_output(Operation, |
| 675 | const TernaryInput<long double> &, |
| 676 | long double, double, RoundingMode); |
| 677 | |
| 678 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 679 | template bool |
| 680 | compare_ternary_operation_one_output(Operation, const TernaryInput<float16> &, |
| 681 | float16, double, RoundingMode); |
| 682 | template bool compare_ternary_operation_one_output(Operation, |
| 683 | const TernaryInput<float> &, |
| 684 | float16, double, |
| 685 | RoundingMode); |
| 686 | template bool compare_ternary_operation_one_output(Operation, |
| 687 | const TernaryInput<double> &, |
| 688 | float16, double, |
| 689 | RoundingMode); |
| 690 | template bool |
| 691 | compare_ternary_operation_one_output(Operation, |
| 692 | const TernaryInput<long double> &, float16, |
| 693 | double, RoundingMode); |
| 694 | #endif |
| 695 | |
| 696 | } // namespace internal |
| 697 | |
| 698 | template <typename T> bool round_to_long(T x, long &result) { |
| 699 | MPFRNumber mpfr(x); |
| 700 | return mpfr.round_to_long(result); |
| 701 | } |
| 702 | |
| 703 | template bool round_to_long<float>(float, long &); |
| 704 | template bool round_to_long<double>(double, long &); |
| 705 | template bool round_to_long<long double>(long double, long &); |
| 706 | |
| 707 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 708 | template bool round_to_long<float16>(float16, long &); |
| 709 | #endif // LIBC_TYPES_HAS_FLOAT16 |
| 710 | |
| 711 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 712 | template bool round_to_long<float128>(float128, long &); |
| 713 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 714 | |
| 715 | template <typename T> bool round_to_long(T x, RoundingMode mode, long &result) { |
| 716 | MPFRNumber mpfr(x); |
| 717 | return mpfr.round_to_long(get_mpfr_rounding_mode(mode), result); |
| 718 | } |
| 719 | |
| 720 | template bool round_to_long<float>(float, RoundingMode, long &); |
| 721 | template bool round_to_long<double>(double, RoundingMode, long &); |
| 722 | template bool round_to_long<long double>(long double, RoundingMode, long &); |
| 723 | |
| 724 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 725 | template bool round_to_long<float16>(float16, RoundingMode, long &); |
| 726 | #endif // LIBC_TYPES_HAS_FLOAT16 |
| 727 | |
| 728 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 729 | template bool round_to_long<float128>(float128, RoundingMode, long &); |
| 730 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 731 | |
| 732 | template <typename T> T round(T x, RoundingMode mode) { |
| 733 | MPFRNumber mpfr(x); |
| 734 | MPFRNumber result = mpfr.rint(get_mpfr_rounding_mode(mode)); |
| 735 | return result.as<T>(); |
| 736 | } |
| 737 | |
| 738 | template float round<float>(float, RoundingMode); |
| 739 | template double round<double>(double, RoundingMode); |
| 740 | template long double round<long double>(long double, RoundingMode); |
| 741 | |
| 742 | #ifdef LIBC_TYPES_HAS_FLOAT16 |
| 743 | template float16 round<float16>(float16, RoundingMode); |
| 744 | #endif // LIBC_TYPES_HAS_FLOAT16 |
| 745 | |
| 746 | #ifdef LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 747 | template float128 round<float128>(float128, RoundingMode); |
| 748 | #endif // LIBC_TYPES_FLOAT128_IS_NOT_LONG_DOUBLE |
| 749 | |
| 750 | } // namespace mpfr |
| 751 | } // namespace testing |
| 752 | } // namespace LIBC_NAMESPACE_DECL |
| 753 | |