xref: /dflybsd-src/contrib/mpfr/src/set_z_exp.c (revision 2786097444a0124b5d33763854de247e230c6629)
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