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 | |