1/* Test file for mpfr_set_ld and mpfr_get_ld.
2
3Copyright 2002-2023 Free Software Foundation, Inc.
4Contributed by the AriC and Caramba projects, INRIA.
5
6This file is part of the GNU MPFR Library.
7
8The GNU MPFR Library is free software; you can redistribute it and/or modify
9it under the terms of the GNU Lesser General Public License as published by
10the Free Software Foundation; either version 3 of the License, or (at your
11option) any later version.
12
13The GNU MPFR Library is distributed in the hope that it will be useful, but
14WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16License for more details.
17
18You should have received a copy of the GNU Lesser General Public License
19along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
20https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
2151 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
22
23#include <float.h>
24
25#include "mpfr-test.h"
26
27static void
28check_gcc33_bug (void)
29{
30  volatile long double x;
31
32  x = (long double) 9007199254740992.0 + 1.0;
33  if (x != 0.0)
34    return;  /* OK */
35  printf
36    ("Detected optimization bug of gcc 3.3 on Alpha concerning long double\n"
37     "comparisons; set_ld tests might fail (set_ld won't work correctly).\n"
38     "See https://gcc.gnu.org/legacy-ml/gcc-bugs/2003-10/msg00853.html for\n"
39     "more information.\n");
40}
41
42static int
43Isnan_ld (long double d)
44{
45  /* Do not convert d to double as this can give an overflow, which
46     may confuse compilers without IEEE 754 support (such as clang
47     -fsanitize=undefined), or trigger a trap if enabled.
48     The DOUBLE_ISNAN macro should work fine on long double. */
49  if (DOUBLE_ISNAN (d))
50    return 1;
51  LONGDOUBLE_NAN_ACTION (d, goto yes);
52  return 0;
53 yes:
54  return 1;
55}
56
57/* Return the minimal number of bits to represent d exactly (0 for zero).
58   If flag is non-zero, also print d. */
59/* FIXME: This function doesn't work if the rounding precision is reduced. */
60static mpfr_prec_t
61print_binary (long double d, int flag)
62{
63  long double e, f, r;
64  long exp = 1;
65  mpfr_prec_t prec = 0;
66
67  if (Isnan_ld (d))
68    {
69      if (flag)
70        printf ("NaN\n");
71      return 0;
72    }
73  if (d < (long double) 0.0
74#if !defined(MPFR_ERRDIVZERO)
75      || (d == (long double) 0.0 && (1.0 / (double) d < 0.0))
76#endif
77      )
78    {
79      if (flag)
80        printf ("-");
81      d = -d;
82    }
83  /* now d >= 0 */
84  /* Use 2 different tests for Inf, to avoid potential bugs
85     in implementations. */
86  if (Isnan_ld (d - d) || (d > 1 && d * 0.5 == d))
87    {
88      if (flag)
89        printf ("Inf\n");
90      return 0;
91    }
92  if (d == (long double) 0.0)
93    {
94      if (flag)
95        printf ("0.0\n");
96      return prec;
97    }
98  MPFR_ASSERTN (d > 0);
99  e = (long double) 1.0;
100  while (e > d)
101    {
102      e = e * (long double) 0.5;
103      exp --;
104    }
105  if (flag == 2) printf ("1: e=%.36Le\n", e);
106  MPFR_ASSERTN (d >= e);
107  /* FIXME: There can be an overflow here, which may not be supported
108     on all platforms. */
109  while (f = e + e, d >= f)
110    {
111      e = f;
112      exp ++;
113    }
114  if (flag == 2) printf ("2: e=%.36Le\n", e);
115  MPFR_ASSERTN (e <= d && d < f);
116  if (flag == 1)
117    printf ("0.");
118  if (flag == 2) printf ("3: d=%.36Le e=%.36Le prec=%ld\n", d, e,
119                         (long) prec);
120  /* Note: the method we use here to extract the bits of d is the following,
121     to deal with the case where the rounding precision is less than the
122     precision of d:
123     (1) we accumulate the upper bits of d into f
124     (2) when accumulating a new bit into f is not exact, we subtract
125         f from d and reset f to 0
126     This is guaranteed to work only when the rounding precision is at least
127     half the precision of d, since otherwise d-f might not be exact.
128     This method does not work with flush-to-zero on underflow. */
129  f = 0.0; /* will hold accumulated powers of 2 */
130  while (1)
131    {
132      prec++;
133      r = f + e;
134      /* r is close to f (in particular in the cases where f+e may
135         not be exact), so that r - f should be exact. */
136      if (r - f != e) /* f+e is not exact */
137        {
138          d -= f; /* should be exact */
139          f = 0.0;
140          r = e;
141        }
142      if (d >= r)
143        {
144          if (flag == 1)
145            printf ("1");
146          if (d == r)
147            break;
148          f = r;
149        }
150      else
151        {
152          if (flag == 1)
153            printf ("0");
154        }
155      e *= (long double) 0.5;
156      MPFR_ASSERTN (e != 0); /* may fail with flush-to-zero on underflow */
157      if (flag == 2) printf ("4: d=%.36Le e=%.36Le prec=%ld\n", d, e,
158                             (long) prec);
159    }
160  if (flag == 1)
161    printf ("e%ld\n", exp);
162  return prec;
163}
164
165/* Checks that a long double converted exactly to a MPFR number, then
166   converted back to a long double gives the initial value, or in other
167   words, mpfr_get_ld(mpfr_set_ld(d)) = d.
168*/
169static void
170check_set_get (long double d)
171{
172  mpfr_exp_t emin, emax;
173  mpfr_t x;
174  mpfr_prec_t prec;
175  int r;
176  long double e;
177  int inex;
178  int red;
179
180  emin = mpfr_get_emin ();
181  emax = mpfr_get_emax ();
182
183  /* Select a precision to ensure that the conversion of d to x be exact. */
184  prec = print_binary (d, 0);
185  if (prec < MPFR_PREC_MIN)
186    prec = MPFR_PREC_MIN;
187  mpfr_init2 (x, prec);
188
189  RND_LOOP(r)
190    {
191      inex = mpfr_set_ld (x, d, (mpfr_rnd_t) r);
192      if (inex != 0)
193        {
194          printf ("Error: mpfr_set_ld should be exact (rnd = %s)\n",
195                  mpfr_print_rnd_mode ((mpfr_rnd_t) r));
196          /* We use 36 digits here, as the maximum LDBL_MANT_DIG value
197             seen in the current implementations is 113 (binary128),
198             and ceil(1+113*log(2)/log(10)) = 36. But the current glibc
199             implementation of printf with double-double arithmetic
200             (e.g. on PowerPC) is not accurate. */
201          printf ("  d ~= %.36Le (output may be wrong!)\n", d);
202          printf ("  inex = %d\n", inex);
203          if (emin >= LONG_MIN)
204            printf ("  emin = %ld\n", (long) emin);
205          if (emax <= LONG_MAX)
206            printf ("  emax = %ld\n", (long) emax);
207          ld_trace ("  d", d);
208          printf ("  d = ");
209          print_binary (d, 1);
210          printf ("  x = ");
211          mpfr_dump (x);
212          printf ("  MPFR_LDBL_MANT_DIG=%u\n", MPFR_LDBL_MANT_DIG);
213          printf ("  prec=%ld\n", (long) prec);
214          print_binary (d, 2);
215          exit (1);
216        }
217      for (red = 0; red < 2; red++)
218        {
219          if (red)
220            {
221              mpfr_exp_t ex;
222
223              if (MPFR_IS_SINGULAR (x))
224                break;
225              ex = MPFR_GET_EXP (x);
226              set_emin (ex);
227              set_emax (ex);
228            }
229          e = mpfr_get_ld (x, (mpfr_rnd_t) r);
230          set_emin (emin);
231          set_emax (emax);
232          if (inex == 0 && ((Isnan_ld(d) && ! Isnan_ld(e)) ||
233                            (Isnan_ld(e) && ! Isnan_ld(d)) ||
234                            (e != d && !(Isnan_ld(e) && Isnan_ld(d)))))
235            {
236              printf ("Error: mpfr_get_ld o mpfr_set_ld <> Id%s\n",
237                      red ? ", reduced exponent range" : "");
238              printf ("  rnd = %s\n", mpfr_print_rnd_mode ((mpfr_rnd_t) r));
239              printf ("  d ~= %.36Le (output may be wrong!)\n", d);
240              printf ("  e ~= %.36Le (output may be wrong!)\n", e);
241              ld_trace ("  d", d);
242              printf ("  x = ");
243              mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN);
244              printf ("\n");
245              ld_trace ("  e", e);
246              printf ("  d = ");
247              print_binary (d, 1);
248              printf ("  x = ");
249              mpfr_dump (x);
250              printf ("  e = ");
251              print_binary (e, 1);
252              printf ("  MPFR_LDBL_MANT_DIG=%u\n", MPFR_LDBL_MANT_DIG);
253#ifdef MPFR_NANISNAN
254              if (Isnan_ld(d) || Isnan_ld(e))
255                printf ("The reason is that NAN == NAN. Please look at the "
256                        "configure output\nand Section \"In case of problem\""
257                        " of the INSTALL file.\n");
258#endif
259              exit (1);
260            }
261        }
262    }
263
264  mpfr_clear (x);
265}
266
267static void
268test_small (void)
269{
270  mpfr_t x, y, z;
271  long double d;
272
273  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);
274  mpfr_init2 (y, MPFR_LDBL_MANT_DIG);
275  mpfr_init2 (z, MPFR_LDBL_MANT_DIG);
276
277  /* x = 11906603631607553907/2^(16381+64) */
278  mpfr_set_str (x, "0.1010010100111100110000001110101101000111010110000001111101110011E-16381", 2, MPFR_RNDN);
279  d = mpfr_get_ld (x, MPFR_RNDN);  /* infinite loop? */
280  mpfr_set_ld (y, d, MPFR_RNDN);
281  mpfr_sub (z, x, y, MPFR_RNDN);
282  mpfr_abs (z, z, MPFR_RNDN);
283  mpfr_clear_erangeflag ();
284  /* If long double = double, d should be equal to 0;
285     in this case, everything is OK. */
286  if (d != 0 && (mpfr_cmp_str (z, "1E-16434", 2, MPFR_RNDN) > 0 ||
287                 mpfr_erangeflag_p ()))
288    {
289      printf ("Error with x = ");
290      mpfr_out_str (stdout, 10, 21, x, MPFR_RNDN);
291      printf (" = ");
292      mpfr_out_str (stdout, 16, 0, x, MPFR_RNDN);
293      printf ("\n        -> d = %.33Le", d);
294      printf ("\n        -> y = ");
295      mpfr_out_str (stdout, 10, 21, y, MPFR_RNDN);
296      printf (" = ");
297      mpfr_out_str (stdout, 16, 0, y, MPFR_RNDN);
298      printf ("\n        -> |x-y| = ");
299      mpfr_out_str (stdout, 16, 0, z, MPFR_RNDN);
300      printf ("\n");
301      exit (1);
302    }
303
304  mpfr_clear (x);
305  mpfr_clear (y);
306  mpfr_clear (z);
307}
308
309static void
310test_fixed_bugs (void)
311{
312  mpfr_t x;
313  long double l, m;
314
315  /* bug found by Steve Kargl (2009-03-14) */
316  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);
317  mpfr_set_ui_2exp (x, 1, -16447, MPFR_RNDN);
318  mpfr_get_ld (x, MPFR_RNDN);  /* an assertion failed in init2.c:50 */
319
320  /* bug reported by Jakub Jelinek (2010-10-17)
321     https://gforge.inria.fr/tracker/?func=detail&aid=11300 */
322  mpfr_set_prec (x, MPFR_LDBL_MANT_DIG);
323  /* l = 0x1.23456789abcdef0123456789abcdp-914L; */
324  l = 8.215640181713713164092636634579e-276;
325  mpfr_set_ld (x, l, MPFR_RNDN);
326  m = mpfr_get_ld (x, MPFR_RNDN);
327  if (m != l)
328    {
329      printf ("Error in get_ld o set_ld for l=%Le\n", l);
330      printf ("Got m=%Le instead of l\n", m);
331      exit (1);
332    }
333
334  /* another similar test which failed with extended double precision and the
335     generic code for mpfr_set_ld */
336  /* l = 0x1.23456789abcdef0123456789abcdp-968L; */
337  l = 4.560596445887084662336528403703e-292;
338  mpfr_set_ld (x, l, MPFR_RNDN);
339  m = mpfr_get_ld (x, MPFR_RNDN);
340  if (m != l)
341    {
342      printf ("Error in get_ld o set_ld for l=%Le\n", l);
343      printf ("Got m=%Le instead of l\n", m);
344      exit (1);
345    }
346
347  mpfr_clear (x);
348}
349
350static void
351check_subnormal (void)
352{
353  long double d, e;
354  mpfr_t x;
355
356  d = 17.0;
357  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);
358  while (d != 0.0)
359    {
360      mpfr_set_ld (x, d, MPFR_RNDN);
361      e = mpfr_get_ld (x, MPFR_RNDN);
362      if (e != d)
363        {
364          printf ("Error in check_subnormal for mpfr_get_ld o mpfr_set_ld\n");
365          printf ("d=%.30Le\n", d);
366          printf ("x="); mpfr_dump (x);
367          printf ("e=%.30Le\n", e);
368          exit (1);
369        }
370      d *= 0.5;
371    }
372  mpfr_clear (x);
373}
374
375static void
376check_overflow (void)
377{
378  long double d, e;
379  mpfr_t x;
380  int i;
381
382  mpfr_init2 (x, MPFR_LDBL_MANT_DIG);
383  for (i = 0; i < 2; i++)
384    {
385      d = i == 0 ? LDBL_MAX : -LDBL_MAX;
386      mpfr_set_ld (x, d, MPFR_RNDN);
387      mpfr_mul_2ui (x, x, 1, MPFR_RNDN);
388      e = mpfr_get_ld (x, MPFR_RNDN);
389      if (! DOUBLE_ISINF (e) || (i == 0 ? (e < 0) : (e > 0)))
390        {
391          printf ("Error in check_overflow.\n");
392          printf ("d=%Le\n", d);
393          printf ("x="); mpfr_dump (x);
394          printf ("e=%Le\n", e);
395          exit (1);
396        }
397    }
398  mpfr_clear (x);
399}
400
401/* issue reported by Sisyphus on powerpc */
402static void
403test_20140212 (void)
404{
405  mpfr_t fr1, fr2;
406  long double ld, h, l, ld2;
407  int i, c1, c2;
408
409  mpfr_init2 (fr1, 106);
410  mpfr_init2 (fr2, 2098);
411
412  for (h = 1.0L, i = 0; i < 1023; i++)
413    h *= 2.0L;
414  for (l = 1.0L, i = 0; i < 1074; i++)
415    l *= 0.5L;
416  ld = h + l; /* rounding of 2^1023 + 2^(-1074) */
417
418  mpfr_set_ld (fr1, ld, MPFR_RNDN);
419  mpfr_set_ld (fr2, ld, MPFR_RNDN);
420
421  c1 = mpfr_cmp_ld (fr1, ld);
422  c2 = mpfr_cmp_ld (fr2, ld);
423
424  /* If long double is binary64, then ld = fr1 = fr2 = 2^1023.
425     If long double is double-double, then ld = 2^1023 + 2^(-1074),
426     fr1 = 2^1023 and fr2 = 2^1023 + 2^(-1074) */
427  MPFR_ASSERTN(ld == h ? (c1 == 0) : (c1 < 0));
428
429  MPFR_ASSERTN(c2 == 0);
430
431  ld2 = mpfr_get_ld (fr2, MPFR_RNDN);
432  MPFR_ASSERTN(ld2 == ld);
433
434  mpfr_clear (fr1);
435  mpfr_clear (fr2);
436}
437
438/* bug reported by Walter Mascarenhas
439   https://sympa.inria.fr/sympa/arc/mpfr/2016-09/msg00005.html */
440static void
441bug_20160907 (void)
442{
443#if HAVE_LDOUBLE_IEEE_EXT_LITTLE
444  long double dn, ld;
445  mpfr_t mp;
446  long e;
447  mpfr_long_double_t x;
448
449  /* the following is the encoding of the smallest subnormal number
450     for HAVE_LDOUBLE_IEEE_EXT_LITTLE */
451  x.s.manl = 1;
452  x.s.manh = 0;
453  x.s.expl = 0;
454  x.s.exph = 0;
455  x.s.sign= 0;
456  dn = x.ld;
457  e = -16445;
458  /* dn=2^e is now the smallest subnormal. */
459
460  mpfr_init2 (mp, 64);
461  mpfr_set_ui_2exp (mp, 1, e - 1, MPFR_RNDN);
462  ld = mpfr_get_ld (mp, MPFR_RNDU);
463  /* since mp = 2^(e-1) and ld is rounded upwards, we should have
464     ld = 2^e */
465  if (ld != dn)
466    {
467      printf ("Error, ld = %Le <> dn = %Le\n", ld, dn);
468      printf ("mp=");
469      mpfr_out_str (stdout, 10, 0, mp, MPFR_RNDN);
470      printf ("\n");
471      printf ("mp="); mpfr_dump (mp);
472      exit (1);
473    }
474
475  /* check a few more numbers */
476  for (e = -16446; e <= -16381; e++)
477    {
478      mpfr_set_ui_2exp (mp, 1, e, MPFR_RNDN);
479      ld = mpfr_get_ld (mp, MPFR_RNDU);
480      mpfr_set_ld (mp, ld, MPFR_RNDU);
481      /* mp is 2^e rounded up, thus should be >= 2^e */
482      if (mpfr_cmp_ui_2exp (mp, 1, e) < 0)
483        {
484          if (tests_run_within_valgrind () && MPFR_IS_ZERO (mp))
485            {
486              /* Since this is not a bug in MPFR and it is just caused by
487                 Valgrind, let's output a message and skip the remaining
488                 part of the test without an error. Note that the message
489                 will be not be visible via "make check".
490                 Note that the other tests do not fail probably because
491                 long double has the same behavior as double (which is
492                 allowed by the C standard), but here this is a test that
493                 is specific to x86 extended precision. */
494              printf
495                ("Error in bug_20160907 due to a bug in Valgrind.\n"
496                 "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=890215\n"
497                 "https://bugs.kde.org/show_bug.cgi?id=421262\n");
498              break;
499            }
500          printf ("Error, expected value >= 2^(%ld)\n", e);
501          printf ("got "); mpfr_dump (mp);
502          exit (1);
503        }
504
505      mpfr_set_ui_2exp (mp, 1, e, MPFR_RNDN);
506      ld = mpfr_get_ld (mp, MPFR_RNDD);
507      mpfr_set_ld (mp, ld, MPFR_RNDD);
508      /* mp is 2^e rounded down, thus should be <= 2^e */
509      if (mpfr_cmp_ui_2exp (mp, 1, e) > 0)
510        {
511          printf ("Error, expected value <= 2^(%ld)\n", e);
512          printf ("got "); mpfr_dump (mp);
513          exit (1);
514        }
515    }
516
517  mpfr_clear (mp);
518#endif
519}
520
521int
522main (int argc, char *argv[])
523{
524  volatile long double d, e, maxp2;
525  mpfr_t x;
526  int i;
527  mpfr_exp_t emax;
528
529  tests_start_mpfr ();
530  mpfr_test_init ();
531
532  check_gcc33_bug ();
533  test_fixed_bugs ();
534
535  mpfr_init2 (x, MPFR_LDBL_MANT_DIG + 64);
536
537#if !defined(MPFR_ERRDIVZERO)
538  /* check NaN */
539  mpfr_set_nan (x);
540  d = mpfr_get_ld (x, MPFR_RNDN);
541  check_set_get (d);
542#endif
543
544  /* check +0.0 and -0.0 */
545  d = 0.0;
546  check_set_get (d);
547  d = DBL_NEG_ZERO;
548  check_set_get (d);
549
550  /* check that the sign of -0.0 is set */
551  mpfr_set_ld (x, DBL_NEG_ZERO, MPFR_RNDN);
552  if (MPFR_IS_POS (x))
553    {
554#if defined(HAVE_SIGNEDZ)
555      printf ("Error: sign of -0.0 is not set correctly\n");
556      exit (1);
557#else
558      /* Non IEEE doesn't support negative zero yet */
559      printf ("Warning: sign of -0.0 is not set correctly\n");
560#endif
561    }
562
563#if !defined(MPFR_ERRDIVZERO)
564  /* check +Inf */
565  mpfr_set_inf (x, 1);
566  d = mpfr_get_ld (x, MPFR_RNDN);
567  check_set_get (d);
568
569  /* check -Inf */
570  mpfr_set_inf (x, -1);
571  d = mpfr_get_ld (x, MPFR_RNDN);
572  check_set_get (d);
573#endif
574
575  /* check the largest power of two */
576  maxp2 = 1.0;
577  while (maxp2 < LDBL_MAX / 2.0)
578    maxp2 *= 2.0;
579  check_set_get (maxp2);
580  check_set_get (-maxp2);
581
582  d = LDBL_MAX;
583  e = d / 2.0;
584  if (e != maxp2)  /* false under NetBSD/x86 */
585    {
586      /* d = LDBL_MAX does not have excess precision. */
587      check_set_get (d);
588      check_set_get (-d);
589    }
590
591  /* check the smallest power of two */
592  d = 1.0;
593  while ((e = d / 2.0) != (long double) 0.0 && e != d)
594    d = e;
595  check_set_get (d);
596  check_set_get (-d);
597
598  /* check that 2^i, 2^i+1, 2^i-1 and 2^i-2^(i-2)-1 are correctly converted */
599  d = 1.0;
600  for (i = 1; i < MPFR_LDBL_MANT_DIG + 8; i++)
601    {
602      d = 2.0 * d; /* d = 2^i */
603      check_set_get (d);
604      if (d + 1.0 != d)
605        check_set_get (d + 1.0);
606      else
607        {
608          mpfr_set_ui_2exp (x, 1, i, MPFR_RNDN);
609          mpfr_add_ui (x, x, 1, MPFR_RNDN);
610          e = mpfr_get_ld (x, MPFR_RNDN);
611          check_set_get (e);
612        }
613      if (d - 1.0 != d)
614        check_set_get (d - 1.0);
615      else
616        {
617          mpfr_set_ui_2exp (x, 1, i, MPFR_RNDN);
618          mpfr_sub_ui (x, x, 1, MPFR_RNDN);
619          e = mpfr_get_ld (x, MPFR_RNDN);
620          check_set_get (e);
621        }
622      if (i < 3)
623        continue;
624      /* The following test triggers a failure in r10844 for i = 56,
625         with gcc -mpc64 on x86 (64-bit ABI). */
626      mpfr_set_ui_2exp (x, 3, i-2, MPFR_RNDN);
627      mpfr_sub_ui (x, x, 1, MPFR_RNDN);
628      e = mpfr_get_ld (x, MPFR_RNDN);
629      check_set_get (e);
630    }
631
632  for (i = 0; i < 10000; i++)
633    {
634      mpfr_urandomb (x, RANDS);
635      d = mpfr_get_ld (x, MPFR_RNDN);
636      check_set_get (d);
637    }
638
639  /* check with reduced emax to exercise overflow */
640  emax = mpfr_get_emax ();
641  mpfr_set_prec (x, 2);
642  set_emax (1);
643  mpfr_set_ld (x, (long double) 2.0, MPFR_RNDN);
644  MPFR_ASSERTN(mpfr_inf_p (x) && mpfr_sgn (x) > 0);
645  for (d = (long double) 2.0, i = 0; i < 13; i++, d *= d);
646  /* now d = 2^8192, or an infinity (e.g. with double or double-double) */
647  mpfr_set_ld (x, d, MPFR_RNDN);
648  MPFR_ASSERTN(mpfr_inf_p (x) && mpfr_sgn (x) > 0);
649  set_emax (emax);
650
651  mpfr_clear (x);
652
653  test_small ();
654
655  check_subnormal ();
656#if !defined(MPFR_ERRDIVZERO)
657  check_overflow ();
658#endif
659
660  test_20140212 ();
661  bug_20160907 ();
662
663  tests_end_mpfr ();
664
665  return 0;
666}
667