14a238c70SJohn Marino /* mpfr_set_z_2exp -- set a floating-point number from a multiple-precision
24a238c70SJohn Marino integer and an exponent
34a238c70SJohn Marino
4*ab6d115fSJohn Marino Copyright 1999, 2000, 2001, 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 #define MPFR_NEED_LONGLONG_H
254a238c70SJohn Marino #include "mpfr-impl.h"
264a238c70SJohn Marino
274a238c70SJohn Marino /* set f to the integer z multiplied by 2^e */
284a238c70SJohn Marino int
mpfr_set_z_2exp(mpfr_ptr f,mpz_srcptr z,mpfr_exp_t e,mpfr_rnd_t rnd_mode)294a238c70SJohn Marino mpfr_set_z_2exp (mpfr_ptr f, mpz_srcptr z, mpfr_exp_t e, mpfr_rnd_t rnd_mode)
304a238c70SJohn Marino {
314a238c70SJohn Marino mp_size_t fn, zn, dif, en;
324a238c70SJohn Marino int k, sign_z, inex;
334a238c70SJohn Marino mp_limb_t *fp, *zp;
344a238c70SJohn Marino mpfr_exp_t exp;
354a238c70SJohn Marino
364a238c70SJohn Marino sign_z = mpz_sgn (z);
374a238c70SJohn Marino if (MPFR_UNLIKELY (sign_z == 0)) /* ignore the exponent for 0 */
384a238c70SJohn Marino {
394a238c70SJohn Marino MPFR_SET_ZERO(f);
404a238c70SJohn Marino MPFR_SET_POS(f);
414a238c70SJohn Marino MPFR_RET(0);
424a238c70SJohn Marino }
434a238c70SJohn Marino MPFR_ASSERTD (sign_z == MPFR_SIGN_POS || sign_z == MPFR_SIGN_NEG);
444a238c70SJohn Marino
454a238c70SJohn Marino zn = ABS(SIZ(z)); /* limb size of z */
464a238c70SJohn Marino /* compute en = floor(e/GMP_NUMB_BITS) */
474a238c70SJohn Marino en = (e >= 0) ? e / GMP_NUMB_BITS : (e + 1) / GMP_NUMB_BITS - 1;
484a238c70SJohn Marino MPFR_ASSERTD (zn >= 1);
494a238c70SJohn Marino if (MPFR_UNLIKELY (zn + en > MPFR_EMAX_MAX / GMP_NUMB_BITS + 1))
504a238c70SJohn Marino return mpfr_overflow (f, rnd_mode, sign_z);
514a238c70SJohn Marino /* because zn + en >= MPFR_EMAX_MAX / GMP_NUMB_BITS + 2
524a238c70SJohn Marino implies (zn + en) * GMP_NUMB_BITS >= MPFR_EMAX_MAX + GMP_NUMB_BITS + 1
534a238c70SJohn Marino and exp = zn * GMP_NUMB_BITS + e - k
544a238c70SJohn Marino >= (zn + en) * GMP_NUMB_BITS - k > MPFR_EMAX_MAX */
554a238c70SJohn Marino
564a238c70SJohn Marino fp = MPFR_MANT (f);
574a238c70SJohn Marino fn = MPFR_LIMB_SIZE (f);
584a238c70SJohn Marino dif = zn - fn;
594a238c70SJohn Marino zp = PTR(z);
604a238c70SJohn Marino count_leading_zeros (k, zp[zn-1]);
614a238c70SJohn Marino
624a238c70SJohn Marino /* now zn + en <= MPFR_EMAX_MAX / GMP_NUMB_BITS + 1
634a238c70SJohn Marino thus (zn + en) * GMP_NUMB_BITS <= MPFR_EMAX_MAX + GMP_NUMB_BITS
644a238c70SJohn Marino and exp = zn * GMP_NUMB_BITS + e - k
654a238c70SJohn Marino <= (zn + en) * GMP_NUMB_BITS - k + GMP_NUMB_BITS - 1
664a238c70SJohn Marino <= MPFR_EMAX_MAX + 2 * GMP_NUMB_BITS - 1 */
674a238c70SJohn Marino exp = (mpfr_prec_t) zn * GMP_NUMB_BITS + e - k;
684a238c70SJohn Marino /* The exponent will be exp or exp + 1 (due to rounding) */
694a238c70SJohn Marino if (MPFR_UNLIKELY (exp > __gmpfr_emax))
704a238c70SJohn Marino return mpfr_overflow (f, rnd_mode, sign_z);
714a238c70SJohn Marino if (MPFR_UNLIKELY (exp + 1 < __gmpfr_emin))
724a238c70SJohn Marino return mpfr_underflow (f, rnd_mode == MPFR_RNDN ? MPFR_RNDZ : rnd_mode,
734a238c70SJohn Marino sign_z);
744a238c70SJohn Marino
754a238c70SJohn Marino if (MPFR_LIKELY (dif >= 0))
764a238c70SJohn Marino {
774a238c70SJohn Marino mp_limb_t rb, sb, ulp;
784a238c70SJohn Marino int sh;
794a238c70SJohn Marino
804a238c70SJohn Marino /* number has to be truncated */
814a238c70SJohn Marino if (MPFR_LIKELY (k != 0))
824a238c70SJohn Marino {
834a238c70SJohn Marino mpn_lshift (fp, &zp[dif], fn, k);
844a238c70SJohn Marino if (MPFR_LIKELY (dif > 0))
854a238c70SJohn Marino fp[0] |= zp[dif - 1] >> (GMP_NUMB_BITS - k);
864a238c70SJohn Marino }
874a238c70SJohn Marino else
884a238c70SJohn Marino MPN_COPY (fp, zp + dif, fn);
894a238c70SJohn Marino
904a238c70SJohn Marino /* Compute Rounding Bit and Sticky Bit */
914a238c70SJohn Marino MPFR_UNSIGNED_MINUS_MODULO (sh, MPFR_PREC (f) );
924a238c70SJohn Marino if (MPFR_LIKELY (sh != 0))
934a238c70SJohn Marino {
944a238c70SJohn Marino mp_limb_t mask = MPFR_LIMB_ONE << (sh-1);
954a238c70SJohn Marino mp_limb_t limb = fp[0];
964a238c70SJohn Marino rb = limb & mask;
974a238c70SJohn Marino sb = limb & (mask-1);
984a238c70SJohn Marino ulp = 2*mask;
994a238c70SJohn Marino fp[0] = limb & ~(ulp-1);
1004a238c70SJohn Marino }
1014a238c70SJohn Marino else /* sh == 0 */
1024a238c70SJohn Marino {
1034a238c70SJohn Marino mp_limb_t mask = MPFR_LIMB_ONE << (GMP_NUMB_BITS - 1 - k);
1044a238c70SJohn Marino if (MPFR_LIKELY (dif > 0))
1054a238c70SJohn Marino {
1064a238c70SJohn Marino rb = zp[--dif] & mask;
1074a238c70SJohn Marino sb = zp[dif] & (mask-1);
1084a238c70SJohn Marino }
1094a238c70SJohn Marino else
1104a238c70SJohn Marino rb = sb = 0;
1114a238c70SJohn Marino k = 0;
1124a238c70SJohn Marino ulp = MPFR_LIMB_ONE;
1134a238c70SJohn Marino }
1144a238c70SJohn Marino if (MPFR_UNLIKELY (sb == 0) && MPFR_LIKELY (dif > 0))
1154a238c70SJohn Marino {
1164a238c70SJohn Marino sb = zp[--dif];
1174a238c70SJohn Marino if (MPFR_LIKELY (k != 0))
1184a238c70SJohn Marino sb &= MPFR_LIMB_MASK (GMP_NUMB_BITS - k);
1194a238c70SJohn Marino if (MPFR_UNLIKELY (sb == 0) && MPFR_LIKELY (dif > 0))
1204a238c70SJohn Marino do {
1214a238c70SJohn Marino sb = zp[--dif];
1224a238c70SJohn Marino } while (dif > 0 && sb == 0);
1234a238c70SJohn Marino }
1244a238c70SJohn Marino
1254a238c70SJohn Marino /* Rounding */
1264a238c70SJohn Marino if (MPFR_LIKELY (rnd_mode == MPFR_RNDN))
1274a238c70SJohn Marino {
1284a238c70SJohn Marino if (rb == 0 || MPFR_UNLIKELY (sb == 0 && (fp[0] & ulp) == 0))
1294a238c70SJohn Marino goto trunc;
1304a238c70SJohn Marino else
1314a238c70SJohn Marino goto addoneulp;
1324a238c70SJohn Marino }
1334a238c70SJohn Marino else /* Not Nearest */
1344a238c70SJohn Marino {
1354a238c70SJohn Marino if (MPFR_LIKELY (MPFR_IS_LIKE_RNDZ (rnd_mode, sign_z < 0))
1364a238c70SJohn Marino || MPFR_UNLIKELY ( (sb | rb) == 0 ))
1374a238c70SJohn Marino goto trunc;
1384a238c70SJohn Marino else
1394a238c70SJohn Marino goto addoneulp;
1404a238c70SJohn Marino }
1414a238c70SJohn Marino
1424a238c70SJohn Marino trunc:
1434a238c70SJohn Marino inex = MPFR_LIKELY ((sb | rb) != 0) ? -1 : 0;
1444a238c70SJohn Marino goto end;
1454a238c70SJohn Marino
1464a238c70SJohn Marino addoneulp:
1474a238c70SJohn Marino inex = 1;
1484a238c70SJohn Marino if (MPFR_UNLIKELY (mpn_add_1 (fp, fp, fn, ulp)))
1494a238c70SJohn Marino {
1504a238c70SJohn Marino /* Pow 2 case */
1514a238c70SJohn Marino if (MPFR_UNLIKELY (exp == __gmpfr_emax))
1524a238c70SJohn Marino return mpfr_overflow (f, rnd_mode, sign_z);
1534a238c70SJohn Marino exp ++;
1544a238c70SJohn Marino fp[fn-1] = MPFR_LIMB_HIGHBIT;
1554a238c70SJohn Marino }
1564a238c70SJohn Marino end:
1574a238c70SJohn Marino (void) 0;
1584a238c70SJohn Marino }
1594a238c70SJohn Marino else /* dif < 0: Mantissa F is strictly bigger than z's one */
1604a238c70SJohn Marino {
1614a238c70SJohn Marino if (MPFR_LIKELY (k != 0))
1624a238c70SJohn Marino mpn_lshift (fp - dif, zp, zn, k);
1634a238c70SJohn Marino else
1644a238c70SJohn Marino MPN_COPY (fp - dif, zp, zn);
1654a238c70SJohn Marino /* fill with zeroes */
1664a238c70SJohn Marino MPN_ZERO (fp, -dif);
1674a238c70SJohn Marino inex = 0; /* result is exact */
1684a238c70SJohn Marino }
1694a238c70SJohn Marino
1704a238c70SJohn Marino if (MPFR_UNLIKELY (exp < __gmpfr_emin))
1714a238c70SJohn Marino {
1724a238c70SJohn Marino if (rnd_mode == MPFR_RNDN && inex == 0 && mpfr_powerof2_raw (f))
1734a238c70SJohn Marino rnd_mode = MPFR_RNDZ;
1744a238c70SJohn Marino return mpfr_underflow (f, rnd_mode, sign_z);
1754a238c70SJohn Marino }
1764a238c70SJohn Marino
1774a238c70SJohn Marino MPFR_SET_EXP (f, exp);
1784a238c70SJohn Marino MPFR_SET_SIGN (f, sign_z);
1794a238c70SJohn Marino MPFR_RET (inex*sign_z);
1804a238c70SJohn Marino }
181