14a238c70SJohn Marino /* mpfr_get_ld, mpfr_get_ld_2exp -- convert a multiple precision floating-point
24a238c70SJohn Marino number to a machine long double
34a238c70SJohn Marino
4*ab6d115fSJohn Marino Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
5*ab6d115fSJohn Marino Contributed by the AriC and Caramel projects, INRIA.
64a238c70SJohn Marino
74a238c70SJohn Marino This file is part of the GNU MPFR Library.
84a238c70SJohn Marino
94a238c70SJohn Marino The GNU MPFR Library is free software; you can redistribute it and/or modify
104a238c70SJohn Marino it under the terms of the GNU Lesser General Public License as published by
114a238c70SJohn Marino the Free Software Foundation; either version 3 of the License, or (at your
124a238c70SJohn Marino option) any later version.
134a238c70SJohn Marino
144a238c70SJohn Marino The GNU MPFR Library is distributed in the hope that it will be useful, but
154a238c70SJohn Marino WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
164a238c70SJohn Marino or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
174a238c70SJohn Marino License for more details.
184a238c70SJohn Marino
194a238c70SJohn Marino You should have received a copy of the GNU Lesser General Public License
204a238c70SJohn Marino along with the GNU MPFR Library; see the file COPYING.LESSER. If not, see
214a238c70SJohn Marino http://www.gnu.org/licenses/ or write to the Free Software Foundation, Inc.,
224a238c70SJohn Marino 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */
234a238c70SJohn Marino
244a238c70SJohn Marino #include <float.h>
254a238c70SJohn Marino
264a238c70SJohn Marino #include "mpfr-impl.h"
274a238c70SJohn Marino
284a238c70SJohn Marino #ifndef HAVE_LDOUBLE_IEEE_EXT_LITTLE
294a238c70SJohn Marino
304a238c70SJohn Marino long double
mpfr_get_ld(mpfr_srcptr x,mpfr_rnd_t rnd_mode)314a238c70SJohn Marino mpfr_get_ld (mpfr_srcptr x, mpfr_rnd_t rnd_mode)
324a238c70SJohn Marino {
334a238c70SJohn Marino
344a238c70SJohn Marino if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (x)))
354a238c70SJohn Marino return (long double) mpfr_get_d (x, rnd_mode);
364a238c70SJohn Marino else /* now x is a normal non-zero number */
374a238c70SJohn Marino {
384a238c70SJohn Marino long double r; /* result */
394a238c70SJohn Marino long double m;
404a238c70SJohn Marino double s; /* part of result */
414a238c70SJohn Marino mpfr_exp_t sh; /* exponent shift, so that x/2^sh is in the double range */
424a238c70SJohn Marino mpfr_t y, z;
434a238c70SJohn Marino int sign;
444a238c70SJohn Marino
454a238c70SJohn Marino /* first round x to the target long double precision, so that
464a238c70SJohn Marino all subsequent operations are exact (this avoids double rounding
474a238c70SJohn Marino problems) */
484a238c70SJohn Marino mpfr_init2 (y, MPFR_LDBL_MANT_DIG);
494a238c70SJohn Marino mpfr_init2 (z, MPFR_LDBL_MANT_DIG);
504a238c70SJohn Marino /* Note about the precision of z: even though IEEE_DBL_MANT_DIG is
514a238c70SJohn Marino sufficient, z has been set to the same precision as y so that
524a238c70SJohn Marino the mpfr_sub below calls mpfr_sub1sp, which is faster than the
534a238c70SJohn Marino generic subtraction, even in this particular case (from tests
544a238c70SJohn Marino done by Patrick Pelissier on a 64-bit Core2 Duo against r7285).
554a238c70SJohn Marino But here there is an important cancellation in the subtraction.
564a238c70SJohn Marino TODO: get more information about what has been tested. */
574a238c70SJohn Marino
584a238c70SJohn Marino mpfr_set (y, x, rnd_mode);
594a238c70SJohn Marino sh = MPFR_GET_EXP (y);
604a238c70SJohn Marino sign = MPFR_SIGN (y);
614a238c70SJohn Marino MPFR_SET_EXP (y, 0);
624a238c70SJohn Marino MPFR_SET_POS (y);
634a238c70SJohn Marino
644a238c70SJohn Marino r = 0.0;
654a238c70SJohn Marino do {
664a238c70SJohn Marino s = mpfr_get_d (y, MPFR_RNDN); /* high part of y */
674a238c70SJohn Marino r += (long double) s;
684a238c70SJohn Marino mpfr_set_d (z, s, MPFR_RNDN); /* exact */
694a238c70SJohn Marino mpfr_sub (y, y, z, MPFR_RNDN); /* exact */
704a238c70SJohn Marino } while (!MPFR_IS_ZERO (y));
714a238c70SJohn Marino
724a238c70SJohn Marino mpfr_clear (z);
734a238c70SJohn Marino mpfr_clear (y);
744a238c70SJohn Marino
754a238c70SJohn Marino /* we now have to multiply back by 2^sh */
764a238c70SJohn Marino MPFR_ASSERTD (r > 0);
774a238c70SJohn Marino if (sh != 0)
784a238c70SJohn Marino {
794a238c70SJohn Marino /* An overflow may occurs (example: 0.5*2^1024) */
804a238c70SJohn Marino while (r < 1.0)
814a238c70SJohn Marino {
824a238c70SJohn Marino r += r;
834a238c70SJohn Marino sh--;
844a238c70SJohn Marino }
854a238c70SJohn Marino
864a238c70SJohn Marino if (sh > 0)
874a238c70SJohn Marino m = 2.0;
884a238c70SJohn Marino else
894a238c70SJohn Marino {
904a238c70SJohn Marino m = 0.5;
914a238c70SJohn Marino sh = -sh;
924a238c70SJohn Marino }
934a238c70SJohn Marino
944a238c70SJohn Marino for (;;)
954a238c70SJohn Marino {
964a238c70SJohn Marino if (sh % 2)
974a238c70SJohn Marino r = r * m;
984a238c70SJohn Marino sh >>= 1;
994a238c70SJohn Marino if (sh == 0)
1004a238c70SJohn Marino break;
1014a238c70SJohn Marino m = m * m;
1024a238c70SJohn Marino }
1034a238c70SJohn Marino }
1044a238c70SJohn Marino if (sign < 0)
1054a238c70SJohn Marino r = -r;
1064a238c70SJohn Marino return r;
1074a238c70SJohn Marino }
1084a238c70SJohn Marino }
1094a238c70SJohn Marino
1104a238c70SJohn Marino #else
1114a238c70SJohn Marino
1124a238c70SJohn Marino long double
mpfr_get_ld(mpfr_srcptr x,mpfr_rnd_t rnd_mode)1134a238c70SJohn Marino mpfr_get_ld (mpfr_srcptr x, mpfr_rnd_t rnd_mode)
1144a238c70SJohn Marino {
1154a238c70SJohn Marino mpfr_long_double_t ld;
1164a238c70SJohn Marino mpfr_t tmp;
1174a238c70SJohn Marino int inex;
1184a238c70SJohn Marino MPFR_SAVE_EXPO_DECL (expo);
1194a238c70SJohn Marino
1204a238c70SJohn Marino MPFR_SAVE_EXPO_MARK (expo);
1214a238c70SJohn Marino
1224a238c70SJohn Marino mpfr_init2 (tmp, MPFR_LDBL_MANT_DIG);
1234a238c70SJohn Marino inex = mpfr_set (tmp, x, rnd_mode);
1244a238c70SJohn Marino
1254a238c70SJohn Marino mpfr_set_emin (-16382-63);
1264a238c70SJohn Marino mpfr_set_emax (16384);
1274a238c70SJohn Marino mpfr_subnormalize (tmp, mpfr_check_range (tmp, inex, rnd_mode), rnd_mode);
1284a238c70SJohn Marino mpfr_prec_round (tmp, 64, MPFR_RNDZ); /* exact */
1294a238c70SJohn Marino if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (tmp)))
1304a238c70SJohn Marino ld.ld = (long double) mpfr_get_d (tmp, rnd_mode);
1314a238c70SJohn Marino else
1324a238c70SJohn Marino {
1334a238c70SJohn Marino mp_limb_t *tmpmant;
1344a238c70SJohn Marino mpfr_exp_t e, denorm;
1354a238c70SJohn Marino
1364a238c70SJohn Marino tmpmant = MPFR_MANT (tmp);
1374a238c70SJohn Marino e = MPFR_GET_EXP (tmp);
1384a238c70SJohn Marino /* the smallest normal number is 2^(-16382), which is 0.5*2^(-16381)
1394a238c70SJohn Marino in MPFR, thus any exponent <= -16382 corresponds to a subnormal
1404a238c70SJohn Marino number */
1414a238c70SJohn Marino denorm = MPFR_UNLIKELY (e <= -16382) ? - e - 16382 + 1 : 0;
1424a238c70SJohn Marino #if GMP_NUMB_BITS >= 64
1434a238c70SJohn Marino ld.s.manl = (tmpmant[0] >> denorm);
1444a238c70SJohn Marino ld.s.manh = (tmpmant[0] >> denorm) >> 32;
1454a238c70SJohn Marino #elif GMP_NUMB_BITS == 32
1464a238c70SJohn Marino if (MPFR_LIKELY (denorm == 0))
1474a238c70SJohn Marino {
1484a238c70SJohn Marino ld.s.manl = tmpmant[0];
1494a238c70SJohn Marino ld.s.manh = tmpmant[1];
1504a238c70SJohn Marino }
1514a238c70SJohn Marino else if (denorm < 32)
1524a238c70SJohn Marino {
1534a238c70SJohn Marino ld.s.manl = (tmpmant[0] >> denorm) | (tmpmant[1] << (32 - denorm));
1544a238c70SJohn Marino ld.s.manh = tmpmant[1] >> denorm;
1554a238c70SJohn Marino }
1564a238c70SJohn Marino else /* 32 <= denorm <= 64 */
1574a238c70SJohn Marino {
1584a238c70SJohn Marino ld.s.manl = tmpmant[1] >> (denorm - 32);
1594a238c70SJohn Marino ld.s.manh = 0;
1604a238c70SJohn Marino }
1614a238c70SJohn Marino #else
1624a238c70SJohn Marino # error "GMP_NUMB_BITS must be 32 or >= 64"
1634a238c70SJohn Marino /* Other values have never been supported anyway. */
1644a238c70SJohn Marino #endif
1654a238c70SJohn Marino if (MPFR_LIKELY (denorm == 0))
1664a238c70SJohn Marino {
1674a238c70SJohn Marino ld.s.exph = (e + 0x3FFE) >> 8;
1684a238c70SJohn Marino ld.s.expl = (e + 0x3FFE);
1694a238c70SJohn Marino }
1704a238c70SJohn Marino else
1714a238c70SJohn Marino ld.s.exph = ld.s.expl = 0;
1724a238c70SJohn Marino ld.s.sign = MPFR_IS_NEG (x);
1734a238c70SJohn Marino }
1744a238c70SJohn Marino
1754a238c70SJohn Marino mpfr_clear (tmp);
1764a238c70SJohn Marino MPFR_SAVE_EXPO_FREE (expo);
1774a238c70SJohn Marino return ld.ld;
1784a238c70SJohn Marino }
1794a238c70SJohn Marino
1804a238c70SJohn Marino #endif
1814a238c70SJohn Marino
1824a238c70SJohn Marino /* contributed by Damien Stehle */
1834a238c70SJohn Marino long double
mpfr_get_ld_2exp(long * expptr,mpfr_srcptr src,mpfr_rnd_t rnd_mode)1844a238c70SJohn Marino mpfr_get_ld_2exp (long *expptr, mpfr_srcptr src, mpfr_rnd_t rnd_mode)
1854a238c70SJohn Marino {
1864a238c70SJohn Marino long double ret;
1874a238c70SJohn Marino mpfr_exp_t exp;
1884a238c70SJohn Marino mpfr_t tmp;
1894a238c70SJohn Marino
1904a238c70SJohn Marino if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (src)))
1914a238c70SJohn Marino return (long double) mpfr_get_d_2exp (expptr, src, rnd_mode);
1924a238c70SJohn Marino
1934a238c70SJohn Marino tmp[0] = *src; /* Hack copy mpfr_t */
1944a238c70SJohn Marino MPFR_SET_EXP (tmp, 0);
1954a238c70SJohn Marino ret = mpfr_get_ld (tmp, rnd_mode);
1964a238c70SJohn Marino
1974a238c70SJohn Marino if (MPFR_IS_PURE_FP(src))
1984a238c70SJohn Marino {
1994a238c70SJohn Marino exp = MPFR_GET_EXP (src);
2004a238c70SJohn Marino
2014a238c70SJohn Marino /* rounding can give 1.0, adjust back to 0.5 <= abs(ret) < 1.0 */
2024a238c70SJohn Marino if (ret == 1.0)
2034a238c70SJohn Marino {
2044a238c70SJohn Marino ret = 0.5;
2054a238c70SJohn Marino exp ++;
2064a238c70SJohn Marino }
2074a238c70SJohn Marino else if (ret == -1.0)
2084a238c70SJohn Marino {
2094a238c70SJohn Marino ret = -0.5;
2104a238c70SJohn Marino exp ++;
2114a238c70SJohn Marino }
2124a238c70SJohn Marino
2134a238c70SJohn Marino MPFR_ASSERTN ((ret >= 0.5 && ret < 1.0)
2144a238c70SJohn Marino || (ret <= -0.5 && ret > -1.0));
2154a238c70SJohn Marino MPFR_ASSERTN (exp >= LONG_MIN && exp <= LONG_MAX);
2164a238c70SJohn Marino }
2174a238c70SJohn Marino else
2184a238c70SJohn Marino exp = 0;
2194a238c70SJohn Marino
2204a238c70SJohn Marino *expptr = exp;
2214a238c70SJohn Marino return ret;
2224a238c70SJohn Marino }
223