1 /* Test file for mpfr_custom_*
2
3 Copyright 2005-2023 Free Software Foundation, Inc.
4 Contributed by the AriC and Caramba projects, INRIA.
5
6 This file is part of the GNU MPFR Library.
7
8 The GNU MPFR Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12
13 The GNU MPFR Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
16 License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
20 https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
21 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23 #include "mpfr-test.h"
24
25 /*************************************************************************/
26
27 /* Bug found on 2022-05-05 in the mpfr_custom_get_size macro.
28 With the buggy macro, this test will fail if mpfr_prec_t > int,
29 typically on LP64 platforms (usual 64-bit machines).
30 This bug could occur only for precisions close to INT_MAX or higher. */
31 static void
test_get_size(void)32 test_get_size (void)
33 {
34 unsigned int u = UINT_MAX;
35 int i = INT_MAX;
36
37 if (u <= MPFR_PREC_MAX)
38 {
39 mpfr_prec_t p = u;
40 size_t s1, s2;
41
42 s1 = mpfr_custom_get_size (p);
43 s2 = mpfr_custom_get_size (u);
44 if (s1 != s2)
45 {
46 printf ("Error 1 in test_get_size (mpfr_custom_get_size macro).\n");
47 printf ("Expected %lu\n", (unsigned long) s1);
48 printf ("Got %lu\n", (unsigned long) s2);
49 exit (1);
50 }
51 }
52
53 if (i <= MPFR_PREC_MAX)
54 {
55 mpfr_prec_t p = i;
56 size_t s1, s2;
57
58 s1 = mpfr_custom_get_size (p);
59 s2 = mpfr_custom_get_size (i);
60 if (s1 != s2)
61 {
62 printf ("Error 2 in test_get_size (mpfr_custom_get_size macro).\n");
63 printf ("Expected %lu\n", (unsigned long) s1);
64 printf ("Got %lu\n", (unsigned long) s2);
65 exit (1);
66 }
67 }
68 }
69
70 /*************************************************************************/
71
72 #define BUFFER_SIZE 250
73 #define PREC_TESTED 200
74
75 long Buffer[BUFFER_SIZE];
76 char *stack = (char *) Buffer;
77 long *org = (long *) Buffer;
78 mpfr_prec_t p = PREC_TESTED;
79
80 #define ALIGNED(s) (((s) + sizeof (long) - 1) / sizeof (long) * sizeof (long))
81
82 /* This code ensures alignment to "long". However, this might not be
83 sufficient on some platforms. GCC's -Wcast-align=strict option can
84 be useful, but this needs successive casts to help GCC, e.g.
85
86 newx = (mpfr_ptr) (long *) (void *) old_stack;
87
88 successively casts old_stack (of type char *) to
89 - void *: avoid a false positive for the following cast to long *
90 (as the code takes care of alignment to "long");
91 - long *: this corresponds to the alignment checked by MPFR; coming
92 from void *, it will not trigger a warning (even if incorrect);
93 - mpfr_ptr: -Wcast-align=strict will emit a warning if mpfr_ptr has
94 an alignment requirement stronger than long *. In such a case,
95 the code will have to be fixed.
96 */
97
98 static void *
new_st(size_t s)99 new_st (size_t s)
100 {
101 void *p = (void *) stack;
102
103 if (MPFR_UNLIKELY (s > (char *) &Buffer[BUFFER_SIZE] - stack))
104 {
105 printf ("[INTERNAL TEST ERROR] Stack overflow.\n");
106 exit (1);
107 }
108 stack += ALIGNED (s);
109 return p;
110 }
111
112 static void
reset_stack(void)113 reset_stack (void)
114 {
115 stack = (char *) Buffer;
116 }
117
118 /*************************************************************************/
119
120 /* Alloc a new mpfr_t on the main stack */
121 static mpfr_ptr
new_mpfr(mpfr_prec_t p)122 new_mpfr (mpfr_prec_t p)
123 {
124 mpfr_ptr x = (mpfr_ptr) new_st (sizeof (mpfr_t));
125 void *mantissa = new_st (mpfr_custom_get_size (p));
126
127 mpfr_custom_init (mantissa, p);
128 mpfr_custom_init_set (x, 0, 0, p, mantissa);
129 return x;
130 }
131
132 /* Alloc a new mpfr_t on the main stack */
133 static mpfr_ptr
new_nan1(mpfr_prec_t p)134 new_nan1 (mpfr_prec_t p)
135 {
136 mpfr_ptr x = (mpfr_ptr) new_st (sizeof (mpfr_t));
137 void *mantissa = new_st ((mpfr_custom_get_size) (p));
138
139 (mpfr_custom_init) (mantissa, p);
140 (mpfr_custom_init_set) (x, MPFR_NAN_KIND, 0, p, mantissa);
141 return x;
142 }
143
144 /* Alloc a new mpfr_t on the main stack */
145 static mpfr_ptr
new_nan2(mpfr_prec_t p)146 new_nan2 (mpfr_prec_t p)
147 {
148 mpfr_ptr x = (mpfr_ptr) new_st (sizeof (mpfr_t));
149 void *mantissa = new_st ((mpfr_custom_get_size) (p));
150 int i1, i2, i3, i4, i5;
151
152 /* Check side effects. */
153 i1 = i2 = 0;
154 mpfr_custom_init ((i1++, mantissa), (i2++, p));
155 MPFR_ASSERTN (i1 == 1);
156 MPFR_ASSERTN (i2 == 1);
157
158 /* Check that the type "void *" can be used in C, like with the function
159 (forbidden in C++). Also check side effects. */
160 i1 = i2 = i3 = i4 = i5 = 0;
161 #ifdef IGNORE_CPP_COMPAT
162 #pragma GCC diagnostic push
163 #pragma GCC diagnostic ignored "-Wc++-compat"
164 #endif
165 mpfr_custom_init_set ((i1++, VOIDP_CAST(x)),
166 (i2++, MPFR_NAN_KIND),
167 (i3++, 0),
168 (i4++, p),
169 (i5++, mantissa));
170 #ifdef IGNORE_CPP_COMPAT
171 #pragma GCC diagnostic pop
172 #endif
173 MPFR_ASSERTN (i1 == 1);
174 MPFR_ASSERTN (i2 == 1);
175 MPFR_ASSERTN (i3 == 1);
176 MPFR_ASSERTN (i4 == 1);
177 MPFR_ASSERTN (i5 == 1);
178
179 return x;
180 }
181
182 /* Alloc a new mpfr_t on the main stack */
183 static mpfr_ptr
new_inf(mpfr_prec_t p)184 new_inf (mpfr_prec_t p)
185 {
186 mpfr_ptr x = (mpfr_ptr) new_st (sizeof (mpfr_t));
187 void *mantissa = new_st ((mpfr_custom_get_size) (p));
188
189 (mpfr_custom_init) (mantissa, p);
190 (mpfr_custom_init_set) (x, -MPFR_INF_KIND, 0, p, mantissa);
191 return x;
192 }
193
194 /* Garbage the stack by keeping only x and save it in old_stack */
195 static mpfr_ptr
return_mpfr(mpfr_ptr x,char * old_stack)196 return_mpfr (mpfr_ptr x, char *old_stack)
197 {
198 void *mantissa = mpfr_custom_get_significand (x);
199 size_t size_mantissa = mpfr_custom_get_size (mpfr_get_prec (x));
200 mpfr_ptr newx;
201 long *newx2;
202 int i1, i2;
203
204 /* Check that the type "void *" can be used in C, like with the function
205 (forbidden in C++). Also check side effects. */
206 i1 = 0;
207 #ifdef IGNORE_CPP_COMPAT
208 #pragma GCC diagnostic push
209 #pragma GCC diagnostic ignored "-Wc++-compat"
210 #endif
211 MPFR_ASSERTN (mpfr_custom_get_significand ((i1++, VOIDP_CAST(x)))
212 == mantissa);
213 #ifdef IGNORE_CPP_COMPAT
214 #pragma GCC diagnostic pop
215 #endif
216 MPFR_ASSERTN (i1 == 1);
217
218 memmove (old_stack, x, sizeof (mpfr_t));
219 memmove (old_stack + ALIGNED (sizeof (mpfr_t)), mantissa, size_mantissa);
220 newx = (mpfr_ptr) (long *) (void *) old_stack;
221 newx2 = (long *) (void *) (old_stack + ALIGNED (sizeof (mpfr_t)));
222
223 /* Check that the type "void *" can be used in C, like with the function
224 (forbidden in C++). Also check side effects. */
225 i1 = i2 = 0;
226 #ifdef IGNORE_CPP_COMPAT
227 #pragma GCC diagnostic push
228 #pragma GCC diagnostic ignored "-Wc++-compat"
229 #endif
230 mpfr_custom_move ((i1++, VOIDP_CAST(newx)), (i2++, newx2));
231 #ifdef IGNORE_CPP_COMPAT
232 #pragma GCC diagnostic pop
233 #endif
234 MPFR_ASSERTN (i1 == 1);
235 MPFR_ASSERTN (i2 == 1);
236
237 stack = (char *) newx2 + ALIGNED (size_mantissa);
238 return newx;
239 }
240
241 /* Garbage the stack by keeping only x and save it in old_stack */
242 static mpfr_ptr
return_mpfr_func(mpfr_ptr x,char * old_stack)243 return_mpfr_func (mpfr_ptr x, char *old_stack)
244 {
245 void *mantissa = (mpfr_custom_get_significand) (x);
246 size_t size_mantissa = (mpfr_custom_get_size) (mpfr_get_prec (x));
247 mpfr_ptr newx;
248
249 memmove (old_stack, x, sizeof (mpfr_t));
250 memmove (old_stack + ALIGNED (sizeof (mpfr_t)), mantissa, size_mantissa);
251 newx = (mpfr_ptr) (long *) (void *) old_stack;
252 (mpfr_custom_move) (newx, old_stack + ALIGNED (sizeof (mpfr_t)));
253 stack = old_stack + ALIGNED (sizeof (mpfr_t)) + ALIGNED (size_mantissa);
254 return newx;
255 }
256
257 /*************************************************************************/
258
259 static void
test1(void)260 test1 (void)
261 {
262 mpfr_ptr x, y;
263
264 reset_stack ();
265 org = (long *) (void *) stack;
266
267 x = new_mpfr (p);
268 y = new_mpfr (p);
269 mpfr_set_ui (x, 42, MPFR_RNDN);
270 mpfr_set_ui (y, 17, MPFR_RNDN);
271 mpfr_add (y, x, y, MPFR_RNDN);
272 y = return_mpfr (y, (char *) org);
273 if ((long *) y != org || mpfr_cmp_ui (y, 59) != 0)
274 {
275 printf ("Compact (1) failed!\n");
276 exit (1);
277 }
278
279 x = new_mpfr (p);
280 y = new_mpfr (p);
281 mpfr_set_ui (x, 4217, MPFR_RNDN);
282 mpfr_set_ui (y, 1742, MPFR_RNDN);
283 mpfr_add (y, x, y, MPFR_RNDN);
284 y = return_mpfr_func (y, (char *) org);
285 if ((long *) y != org || mpfr_cmp_ui (y, 5959) != 0)
286 {
287 printf ("Compact (5) failed!\n");
288 exit (1);
289 }
290
291 reset_stack ();
292 }
293
294 static void
test_nan_inf_zero(void)295 test_nan_inf_zero (void)
296 {
297 mpfr_ptr val;
298 mpfr_srcptr sval; /* for compilation error checking */
299 int sign;
300 int kind;
301
302 reset_stack ();
303
304 val = new_mpfr (MPFR_PREC_MIN);
305 sval = val;
306 mpfr_set_nan (val);
307 kind = (mpfr_custom_get_kind) (sval);
308 if (kind != MPFR_NAN_KIND)
309 {
310 printf ("mpfr_custom_get_kind error: ");
311 mpfr_dump (val);
312 printf (" is kind %d instead of %d\n", kind, (int) MPFR_NAN_KIND);
313 exit (1);
314 }
315
316 val = new_nan1 (MPFR_PREC_MIN);
317 if (!mpfr_nan_p(val))
318 {
319 printf ("Error: mpfr_custom_init_set doesn't set NAN mpfr (1).\n");
320 exit (1);
321 }
322
323 val = new_nan2 (MPFR_PREC_MIN);
324 if (!mpfr_nan_p(val))
325 {
326 printf ("Error: mpfr_custom_init_set doesn't set NAN mpfr (2).\n");
327 exit (1);
328 }
329
330 val = new_inf (MPFR_PREC_MIN);
331 if (!mpfr_inf_p(val) || mpfr_sgn(val) >= 0)
332 {
333 printf ("Error: mpfr_custom_init_set doesn't set -INF mpfr.\n");
334 exit (1);
335 }
336
337 sign = 1;
338 mpfr_set_inf (val, sign);
339 kind = (mpfr_custom_get_kind) (val);
340 if ((ABS (kind) != MPFR_INF_KIND) || (VSIGN (kind) != VSIGN (sign)))
341 {
342 printf ("mpfr_custom_get_kind error: ");
343 mpfr_dump (val);
344 printf (" is kind %d instead of %d\n", kind, (int) MPFR_INF_KIND);
345 printf (" have sign %d instead of %d\n", VSIGN (kind), VSIGN (sign));
346 exit (1);
347 }
348
349 sign = -1;
350 mpfr_set_zero (val, sign);
351 kind = (mpfr_custom_get_kind) (val);
352 if ((ABS (kind) != MPFR_ZERO_KIND) || (VSIGN (kind) != VSIGN (sign)))
353 {
354 printf ("mpfr_custom_get_kind error: ");
355 mpfr_dump (val);
356 printf (" is kind %d instead of %d\n", kind, (int) MPFR_ZERO_KIND);
357 printf (" have sign %d instead of %d\n", VSIGN (kind), VSIGN (sign));
358 exit (1);
359 }
360
361 reset_stack ();
362 }
363
364 /*************************************************************************/
365
366 /* We build the MPFR variable each time it is needed */
367 /* a[0] is the kind, a[1] is the exponent, &a[2] is the mantissa */
368 static long *
dummy_new(void)369 dummy_new (void)
370 {
371 long *r;
372
373 r = (long *) new_st (ALIGNED (2 * sizeof (long)) +
374 ALIGNED (mpfr_custom_get_size (p)));
375 (mpfr_custom_init) (&r[2], p);
376 r[0] = (int) MPFR_NAN_KIND;
377 r[1] = 0;
378 return r;
379 }
380
381 static long *
dummy_set_si(long si)382 dummy_set_si (long si)
383 {
384 mpfr_t x;
385 mpfr_srcptr px; /* for compilation error checking */
386 long *r = dummy_new ();
387 int i1, i2, i3, i4, i5;
388
389 /* Check that the type "void *" can be used, like with the function.
390 Also check side effects. */
391 i1 = i2 = i3 = i4 = i5 = 0;
392 #ifdef IGNORE_CPP_COMPAT
393 #pragma GCC diagnostic push
394 #pragma GCC diagnostic ignored "-Wc++-compat"
395 #endif
396 mpfr_custom_init_set ((i1++, VOIDP_CAST(x)),
397 (i2++, MPFR_REGULAR_KIND),
398 (i3++, 0),
399 (i4++, p),
400 (i5++, &r[2]));
401 #ifdef IGNORE_CPP_COMPAT
402 #pragma GCC diagnostic pop
403 #endif
404 MPFR_ASSERTN (i1 == 1);
405 MPFR_ASSERTN (i2 == 1);
406 MPFR_ASSERTN (i3 == 1);
407 MPFR_ASSERTN (i4 == 1);
408 MPFR_ASSERTN (i5 == 1);
409
410 mpfr_set_si (x, si, MPFR_RNDN);
411 px = x;
412 r[0] = mpfr_custom_get_kind (px);
413
414 /* Check that the type "void *" can be used in C, like with the function
415 (forbidden in C++). Also check side effects. */
416 i1 = 0;
417 #ifdef IGNORE_CPP_COMPAT
418 #pragma GCC diagnostic push
419 #pragma GCC diagnostic ignored "-Wc++-compat"
420 #endif
421 MPFR_ASSERTN (mpfr_custom_get_kind ((i1++, VOIDP_CAST(x))) == r[0]);
422 #ifdef IGNORE_CPP_COMPAT
423 #pragma GCC diagnostic pop
424 #endif
425 MPFR_ASSERTN (i1 == 1);
426
427 r[1] = mpfr_custom_get_exp (x);
428
429 /* Check that the type "void *" can be used in C, like with the function
430 (forbidden in C++). Also check side effects. */
431 i1 = 0;
432 #ifdef IGNORE_CPP_COMPAT
433 #pragma GCC diagnostic push
434 #pragma GCC diagnostic ignored "-Wc++-compat"
435 #endif
436 MPFR_ASSERTN (mpfr_custom_get_exp ((i1++, VOIDP_CAST(x))) == r[1]);
437 #ifdef IGNORE_CPP_COMPAT
438 #pragma GCC diagnostic pop
439 #endif
440 MPFR_ASSERTN (i1 == 1);
441
442 return r;
443 }
444
445 static long *
dummy_add(long * a,long * b)446 dummy_add (long *a, long *b)
447 {
448 mpfr_t x, y, z;
449 long *r = dummy_new ();
450
451 mpfr_custom_init_set (x, 0 + MPFR_REGULAR_KIND, 0, p, &r[2]);
452 (mpfr_custom_init_set) (y, a[0], a[1], p, &a[2]);
453 mpfr_custom_init_set (z, 0 + b[0], b[1], p, &b[2]);
454 mpfr_add (x, y, z, MPFR_RNDN);
455 r[0] = (mpfr_custom_get_kind) (x);
456 r[1] = (mpfr_custom_get_exp) (x);
457 return r;
458 }
459
460 static long *
dummy_compact(long * r,long * org_stack)461 dummy_compact (long *r, long *org_stack)
462 {
463 memmove (org_stack, r,
464 ALIGNED (2*sizeof (long)) + ALIGNED ((mpfr_custom_get_size) (p)));
465 return org_stack;
466 }
467
468 /*************************************************************************/
469
470 static void
test2(void)471 test2 (void)
472 {
473 mpfr_t x;
474 long *a, *b, *c;
475
476 reset_stack ();
477 org = (long *) (void *) stack;
478
479 a = dummy_set_si (42);
480 b = dummy_set_si (17);
481 c = dummy_add (a, b);
482 c = dummy_compact (c, org);
483 (mpfr_custom_init_set) (x, c[0], c[1], p, &c[2]);
484 if (c != org || mpfr_cmp_ui (x, 59) != 0)
485 {
486 printf ("Compact (2) failed! c=%p a=%p\n", (void *) c, (void *) a);
487 mpfr_dump (x);
488 exit (1);
489 }
490
491 a = dummy_set_si (42);
492 b = dummy_set_si (-17);
493 c = dummy_add (a, b);
494 c = dummy_compact (c, org);
495 (mpfr_custom_init_set) (x, c[0], c[1], p, &c[2]);
496 if (c != org || mpfr_cmp_ui (x, 25) != 0)
497 {
498 printf ("Compact (6) failed! c=%p a=%p\n", (void *) c, (void *) a);
499 mpfr_dump (x);
500 exit (1);
501 }
502
503 reset_stack ();
504 }
505
506
507 int
main(void)508 main (void)
509 {
510 tests_start_mpfr ();
511 test_get_size ();
512 /* We test iff long = mp_limb_t */
513 if (sizeof (long) == sizeof (mp_limb_t))
514 {
515 test1 ();
516 test2 ();
517 test_nan_inf_zero ();
518 }
519 tests_end_mpfr ();
520 return 0;
521 }
522