xref: /netbsd-src/external/lgpl3/mpfr/dist/src/get_float128.c (revision ba125506a622fe649968631a56eba5d42ff57863)
1299c6f0cSmrg /* mpfr_get_float128 -- convert a multiple precision floating-point
22ba2404bSmrg                         number to a _Float128 number
3299c6f0cSmrg 
4*ba125506Smrg Copyright 2012-2023 Free Software Foundation, Inc.
5299c6f0cSmrg Contributed by the AriC and Caramba projects, INRIA.
6299c6f0cSmrg 
7299c6f0cSmrg This file is part of the GNU MPFR Library.
8299c6f0cSmrg 
9299c6f0cSmrg The GNU MPFR Library is free software; you can redistribute it and/or modify
10299c6f0cSmrg it under the terms of the GNU Lesser General Public License as published by
11299c6f0cSmrg the Free Software Foundation; either version 3 of the License, or (at your
12299c6f0cSmrg option) any later version.
13299c6f0cSmrg 
14299c6f0cSmrg The GNU MPFR Library is distributed in the hope that it will be useful, but
15299c6f0cSmrg WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16299c6f0cSmrg or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
17299c6f0cSmrg License for more details.
18299c6f0cSmrg 
19299c6f0cSmrg You should have received a copy of the GNU Lesser General Public License
20299c6f0cSmrg along with the GNU MPFR Library; see the file COPYING.LESSER.  If not, see
212ba2404bSmrg https://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
22299c6f0cSmrg 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
23299c6f0cSmrg 
24299c6f0cSmrg #include "mpfr-impl.h"
25299c6f0cSmrg 
26299c6f0cSmrg #ifdef MPFR_WANT_FLOAT128
27299c6f0cSmrg 
28299c6f0cSmrg /* generic code */
292ba2404bSmrg _Float128
mpfr_get_float128(mpfr_srcptr x,mpfr_rnd_t rnd_mode)30299c6f0cSmrg mpfr_get_float128 (mpfr_srcptr x, mpfr_rnd_t rnd_mode)
31299c6f0cSmrg {
32299c6f0cSmrg 
33299c6f0cSmrg   if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
342ba2404bSmrg     return (_Float128) mpfr_get_d (x, rnd_mode);
35299c6f0cSmrg   else /* now x is a normal non-zero number */
36299c6f0cSmrg     {
372ba2404bSmrg       _Float128 r; /* result */
382ba2404bSmrg       _Float128 m;
39299c6f0cSmrg       mpfr_exp_t e;  /* exponent of x (before rounding) */
40299c6f0cSmrg       mpfr_exp_t sh; /* exponent shift, so that x/2^sh is in the double range */
41299c6f0cSmrg       const int emin = -16381;
42299c6f0cSmrg       const int esub = emin - IEEE_FLOAT128_MANT_DIG;
43299c6f0cSmrg       int sign;
44299c6f0cSmrg 
45299c6f0cSmrg       sign = MPFR_SIGN (x);
46299c6f0cSmrg       e = MPFR_GET_EXP (x);
47299c6f0cSmrg 
48299c6f0cSmrg       if (MPFR_UNLIKELY (e <= esub))
49299c6f0cSmrg         {
50299c6f0cSmrg           if (MPFR_IS_LIKE_RNDZ (rnd_mode, sign < 0) ||
51299c6f0cSmrg               (rnd_mode == MPFR_RNDN && (e < esub || mpfr_powerof2_raw (x))))
52299c6f0cSmrg             return sign < 0 ? -0.0 : 0.0;
53299c6f0cSmrg           r = 1.0;
54299c6f0cSmrg           sh = esub;
55299c6f0cSmrg         }
56299c6f0cSmrg       else
57299c6f0cSmrg         {
58299c6f0cSmrg           mpfr_t y;
59299c6f0cSmrg           mp_limb_t *yp;
60299c6f0cSmrg           int prec, i;  /* small enough to fit in an int */
61299c6f0cSmrg           MPFR_SAVE_EXPO_DECL (expo);
62299c6f0cSmrg 
63299c6f0cSmrg           MPFR_SAVE_EXPO_MARK (expo);
64299c6f0cSmrg 
652ba2404bSmrg           /* First round x to the target _Float128 precision, taking the
66299c6f0cSmrg              reduced precision of the subnormals into account, so that all
67299c6f0cSmrg              subsequent operations are exact (this avoids double rounding
68299c6f0cSmrg              problems). */
69299c6f0cSmrg           prec = e < emin ? e - esub : IEEE_FLOAT128_MANT_DIG;
70299c6f0cSmrg           MPFR_ASSERTD (prec >= MPFR_PREC_MIN);
71299c6f0cSmrg           mpfr_init2 (y, prec);
72299c6f0cSmrg 
73299c6f0cSmrg           mpfr_set (y, x, rnd_mode);
74299c6f0cSmrg           sh = MPFR_GET_EXP (y);
75299c6f0cSmrg           MPFR_SET_EXP (y, 0);
76299c6f0cSmrg           MPFR_SET_POS (y);
77299c6f0cSmrg           yp = MPFR_MANT (y);
78299c6f0cSmrg 
79299c6f0cSmrg           r = 0.0;
80299c6f0cSmrg           for (i = 0; i < MPFR_LIMB_SIZE (y); i++)
81299c6f0cSmrg             {
82299c6f0cSmrg               /* Note: MPFR_LIMB_MAX is avoided below as it might not
83299c6f0cSmrg                  always work if GMP_NUMB_BITS > IEEE_FLOAT128_MANT_DIG.
84299c6f0cSmrg                  MPFR_LIMB_HIGHBIT has the advantage to fit on 1 bit. */
85299c6f0cSmrg               r += yp[i];
862ba2404bSmrg               r *= 1 / (2 * (_Float128) MPFR_LIMB_HIGHBIT);
87299c6f0cSmrg             }
88299c6f0cSmrg 
89299c6f0cSmrg           mpfr_clear (y);
90299c6f0cSmrg 
91299c6f0cSmrg           MPFR_SAVE_EXPO_FREE (expo);
92299c6f0cSmrg         }
93299c6f0cSmrg 
94299c6f0cSmrg       /* we now have to multiply r by 2^sh */
95299c6f0cSmrg       MPFR_ASSERTD (r > 0);
96299c6f0cSmrg       if (sh != 0)
97299c6f0cSmrg         {
98299c6f0cSmrg           /* An overflow may occur (example: 0.5*2^1024) */
99299c6f0cSmrg           while (r < 1.0)
100299c6f0cSmrg             {
101299c6f0cSmrg               r += r;
102299c6f0cSmrg               sh--;
103299c6f0cSmrg             }
104299c6f0cSmrg 
105299c6f0cSmrg           if (sh > 0)
106299c6f0cSmrg             m = 2.0;
107299c6f0cSmrg           else
108299c6f0cSmrg             {
109299c6f0cSmrg               m = 0.5;
110299c6f0cSmrg               sh = -sh;
111299c6f0cSmrg             }
112299c6f0cSmrg 
113299c6f0cSmrg           for (;;)
114299c6f0cSmrg             {
115299c6f0cSmrg               if (sh % 2)
116299c6f0cSmrg                 r = r * m;
117299c6f0cSmrg               sh >>= 1;
118299c6f0cSmrg               if (sh == 0)
119299c6f0cSmrg                 break;
120299c6f0cSmrg               m = m * m;
121299c6f0cSmrg             }
122299c6f0cSmrg         }
123299c6f0cSmrg       if (sign < 0)
124299c6f0cSmrg         r = -r;
125299c6f0cSmrg       return r;
126299c6f0cSmrg     }
127299c6f0cSmrg }
128299c6f0cSmrg 
129299c6f0cSmrg #endif /* MPFR_WANT_FLOAT128 */
130