summaryrefslogtreecommitdiff
path: root/mpfr/round_prec.c
diff options
context:
space:
mode:
Diffstat (limited to 'mpfr/round_prec.c')
-rw-r--r--mpfr/round_prec.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/mpfr/round_prec.c b/mpfr/round_prec.c
new file mode 100644
index 000000000..e0228d87e
--- /dev/null
+++ b/mpfr/round_prec.c
@@ -0,0 +1,318 @@
+/* mpfr_round_raw_generic, mpfr_round_raw2, mpfr_round_raw, mpfr_round_prec,
+ mpfr_can_round, mpfr_can_round_raw -- various rounding functions
+
+Copyright (C) 1999-2002 Free Software Foundation, Inc.
+
+This file is part of the MPFR Library.
+
+The MPFR Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or (at your
+option) any later version.
+
+The MPFR Library is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the MPFR Library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+MA 02111-1307, USA. */
+
+#include "gmp.h"
+#include "gmp-impl.h"
+#include "mpfr.h"
+#include "mpfr-impl.h"
+
+#if (BITS_PER_MP_LIMB & (BITS_PER_MP_LIMB - 1))
+#error "BITS_PER_MP_LIMB must be a power of 2"
+#endif
+
+/*
+ If flag = 0, puts in y the value of xp (with precision xprec and
+ sign 1 if negative=0, -1 otherwise) rounded to precision yprec and
+ direction rnd_mode. Supposes x is not zero nor NaN nor +/- Infinity
+ (i.e. *xp != 0). If inexp != NULL, computes the inexact flag of the
+ rounding.
+
+ In case of even rounding when rnd = GMP_RNDN, returns 2 or -2.
+
+ If flag = 1, just returns whether one should add 1 or not for rounding.
+*/
+
+int
+mpfr_round_raw_generic(mp_limb_t *yp, mp_limb_t *xp, mp_prec_t xprec,
+ int neg, mp_prec_t yprec, mp_rnd_t rnd_mode,
+ int *inexp, int flag)
+{
+ mp_size_t xsize, nw;
+ mp_limb_t himask, lomask;
+ int rw, carry = 0;
+
+ xsize = (xprec-1)/BITS_PER_MP_LIMB + 1;
+
+ nw = yprec / BITS_PER_MP_LIMB;
+ rw = yprec & (BITS_PER_MP_LIMB - 1);
+
+ if (flag && !inexp && (rnd_mode==GMP_RNDZ || xprec <= yprec
+ || (rnd_mode==GMP_RNDU && neg)
+ || (rnd_mode==GMP_RNDD && neg==0)))
+ return 0;
+
+ if (rw)
+ {
+ nw++;
+ lomask = ((MP_LIMB_T_ONE << (BITS_PER_MP_LIMB - rw)) - MP_LIMB_T_ONE);
+ himask = ~lomask;
+ }
+ else
+ {
+ lomask = -1;
+ himask = -1;
+ }
+ MPFR_ASSERTN(nw >= 1);
+
+ if (xprec <= yprec)
+ { /* No rounding is necessary. */
+ /* if yp=xp, maybe an overlap: MPN_COPY_DECR is ok when src <= dst */
+ MPFR_ASSERTN(nw >= xsize);
+ if (inexp)
+ *inexp = 0;
+ if (flag)
+ return 0;
+ MPN_COPY_DECR(yp + (nw - xsize), xp, xsize);
+ MPN_ZERO(yp, nw - xsize);
+ }
+ else
+ {
+ mp_limb_t sb;
+
+ if ((rnd_mode == GMP_RNDU && neg) ||
+ (rnd_mode == GMP_RNDD && !neg))
+ rnd_mode = GMP_RNDZ;
+
+ if (inexp || rnd_mode != GMP_RNDZ)
+ {
+ mp_size_t k;
+
+ k = xsize - nw;
+ if (!rw)
+ k--;
+ MPFR_ASSERTN(k >= 0);
+ sb = xp[k] & lomask; /* First non-significant bits */
+ if (rnd_mode == GMP_RNDN)
+ {
+ mp_limb_t rbmask = MP_LIMB_T_ONE << (BITS_PER_MP_LIMB - rw - 1);
+ if (sb & rbmask) /* rounding bit */
+ sb &= ~rbmask; /* it is 1, clear it */
+ else
+ rnd_mode = GMP_RNDZ; /* it is 0, behave like rounding to 0 */
+ }
+ while (sb == 0 && k > 0)
+ sb = xp[--k];
+ if (rnd_mode == GMP_RNDN)
+ { /* rounding to nearest, with rounding bit = 1 */
+ if (sb == 0) /* Even rounding. */
+ {
+ sb = xp[xsize - nw] & (himask ^ (himask << 1));
+ if (inexp)
+ *inexp = ((neg != 0) ^ (sb != 0))
+ ? MPFR_EVEN_INEX : -MPFR_EVEN_INEX;
+ }
+ else /* sb != 0 */
+ {
+ if (inexp)
+ *inexp = (neg == 0) ? 1 : -1;
+ }
+ }
+ else if (inexp)
+ *inexp = sb == 0 ? 0
+ : (((neg != 0) ^ (rnd_mode != GMP_RNDZ)) ? 1 : -1);
+ }
+ else
+ sb = 0;
+
+ if (flag)
+ return sb != 0 && rnd_mode != GMP_RNDZ;
+
+ if (sb != 0 && rnd_mode != GMP_RNDZ)
+ carry = mpn_add_1(yp, xp + xsize - nw, nw,
+ rw ? MP_LIMB_T_ONE << (BITS_PER_MP_LIMB - rw) : 1);
+ else
+ MPN_COPY_INCR(yp, xp + xsize - nw, nw);
+
+ yp[0] &= himask;
+ }
+
+ return carry;
+}
+
+int
+mpfr_round_prec (mpfr_ptr x, mp_rnd_t rnd_mode, mp_prec_t prec)
+{
+ mp_limb_t *tmp, *xp;
+ int carry, inexact;
+ mp_prec_t nw;
+ TMP_DECL(marker);
+
+ MPFR_ASSERTN(prec >= MPFR_PREC_MIN && prec <= MPFR_PREC_MAX);
+
+ nw = 1 + (prec - 1) / BITS_PER_MP_LIMB; /* needed allocated limbs */
+
+ /* check if x has enough allocated space for the mantissa */
+ if (nw > MPFR_ABSSIZE(x))
+ {
+ MPFR_MANT(x) = (mp_ptr) (*__gmp_reallocate_func)
+ (MPFR_MANT(x), (size_t) MPFR_ABSSIZE(x) * BYTES_PER_MP_LIMB,
+ (size_t) nw * BYTES_PER_MP_LIMB);
+ MPFR_SET_ABSSIZE(x, nw); /* new number of allocated limbs */
+ }
+
+ if (MPFR_IS_NAN(x))
+ MPFR_RET_NAN;
+
+ if (MPFR_IS_INF(x))
+ return 0; /* infinity is exact */
+
+ /* x is a real number */
+
+ TMP_MARK(marker);
+ tmp = TMP_ALLOC (nw * BYTES_PER_MP_LIMB);
+ xp = MPFR_MANT(x);
+ carry = mpfr_round_raw (tmp, xp, MPFR_PREC(x), MPFR_SIGN(x) < 0,
+ prec, rnd_mode, &inexact);
+ MPFR_PREC(x) = prec;
+
+ if (carry)
+ {
+ mp_exp_t exp = MPFR_EXP(x);
+
+ if (exp == __mpfr_emax)
+ (void) mpfr_set_overflow(x, rnd_mode, MPFR_SIGN(x));
+ else
+ {
+ MPFR_EXP(x)++;
+ xp[nw - 1] = MP_LIMB_T_HIGHBIT;
+ if (nw - 1 > 0)
+ MPN_ZERO(xp, nw - 1);
+ }
+ }
+ else
+ MPN_COPY(xp, tmp, nw);
+
+ TMP_FREE(marker);
+ return inexact;
+}
+
+/* assumption: BITS_PER_MP_LIMB is a power of 2 */
+
+/* assuming b is an approximation of x in direction rnd1
+ with error at most 2^(MPFR_EXP(b)-err), returns 1 if one is
+ able to round exactly x to precision prec with direction rnd2,
+ and 0 otherwise.
+
+ Side effects: none.
+*/
+
+int
+mpfr_can_round (mpfr_ptr b, mp_exp_t err, mp_rnd_t rnd1,
+ mp_rnd_t rnd2, mp_prec_t prec)
+{
+ return MPFR_IS_ZERO(b) ? 0 : /* we cannot round if b=0 */
+ mpfr_can_round_raw (MPFR_MANT(b),
+ (MPFR_PREC(b) - 1)/BITS_PER_MP_LIMB + 1,
+ MPFR_SIGN(b), err, rnd1, rnd2, prec);
+}
+
+int
+mpfr_can_round_raw (mp_limb_t *bp, mp_size_t bn, int neg, mp_exp_t err0,
+ mp_rnd_t rnd1, mp_rnd_t rnd2, mp_prec_t prec)
+{
+ mp_prec_t err;
+ mp_size_t k, k1, tn;
+ int s, s1;
+ mp_limb_t cc, cc2;
+ mp_limb_t *tmp;
+ TMP_DECL(marker);
+
+ if (err0 < 0 || (mp_exp_unsigned_t) err0 <= prec)
+ return 0; /* can't round */
+
+ neg = neg <= 0;
+
+ /* if the error is smaller than ulp(b), then anyway it will propagate
+ up to ulp(b) */
+ err = ((mp_exp_unsigned_t) err0 > (mp_prec_t) bn * BITS_PER_MP_LIMB) ?
+ (mp_prec_t) bn * BITS_PER_MP_LIMB : err0;
+
+ /* warning: if k = m*BITS_PER_MP_LIMB, consider limb m-1 and not m */
+ k = (err - 1) / BITS_PER_MP_LIMB;
+ s = err % BITS_PER_MP_LIMB;
+ if (s)
+ s = BITS_PER_MP_LIMB - s;
+ /* the error corresponds to bit s in limb k, the most significant limb
+ being limb 0 */
+ k1 = (prec - 1) / BITS_PER_MP_LIMB;
+ s1 = prec % BITS_PER_MP_LIMB;
+ if (s1)
+ s1 = BITS_PER_MP_LIMB - s1;
+
+ /* the last significant bit is bit s1 in limb k1 */
+
+ /* don't need to consider the k1 most significant limbs */
+ k -= k1;
+ bn -= k1;
+ prec -= (mp_prec_t) k1 * BITS_PER_MP_LIMB;
+ /* if when adding or subtracting (1 << s) in bp[bn-1-k], it does not
+ change bp[bn-1] >> s1, then we can round */
+
+ if (rnd1 == GMP_RNDU)
+ if (neg)
+ rnd1 = GMP_RNDZ;
+
+ if (rnd1 == GMP_RNDD)
+ rnd1 = neg ? GMP_RNDU : GMP_RNDZ;
+
+ /* in the sequel, RNDU = towards infinity, RNDZ = towards zero */
+
+ TMP_MARK(marker);
+ tn = bn;
+ k++; /* since we work with k+1 everywhere */
+ tmp = TMP_ALLOC(tn * BYTES_PER_MP_LIMB);
+ if (bn > k)
+ MPN_COPY (tmp, bp, bn - k);
+
+ if (rnd1 != GMP_RNDN)
+ { /* GMP_RNDZ or GMP_RNDU */
+ cc = (bp[bn - 1] >> s1) & 1;
+ cc ^= mpfr_round_raw2(bp, bn, neg, rnd2, prec);
+
+ /* now round b +/- 2^(MPFR_EXP(b)-err) */
+ cc2 = rnd1 == GMP_RNDZ ?
+ mpn_add_1 (tmp + bn - k, bp + bn - k, k, MP_LIMB_T_ONE << s) :
+ mpn_sub_1 (tmp + bn - k, bp + bn - k, k, MP_LIMB_T_ONE << s);
+ }
+ else
+ { /* GMP_RNDN */
+ /* first round b+2^(MPFR_EXP(b)-err) */
+ cc = mpn_add_1 (tmp + bn - k, bp + bn - k, k, MP_LIMB_T_ONE << s);
+ cc = (tmp[bn - 1] >> s1) & 1; /* gives 0 when cc=1 */
+ cc ^= mpfr_round_raw2 (tmp, bn, neg, rnd2, prec);
+
+ /* now round b-2^(MPFR_EXP(b)-err) */
+ cc2 = mpn_sub_1 (tmp + bn - k, bp + bn - k, k, MP_LIMB_T_ONE << s);
+ }
+
+ if (cc2 && cc)
+ {
+ TMP_FREE(marker);
+ return 0;
+ }
+
+ cc2 = (tmp[bn - 1] >> s1) & 1;
+ cc2 ^= mpfr_round_raw2 (tmp, bn, neg, rnd2, prec);
+
+ TMP_FREE(marker);
+ return cc == cc2;
+}