summaryrefslogtreecommitdiff
path: root/mpfr/add1.c
diff options
context:
space:
mode:
Diffstat (limited to 'mpfr/add1.c')
-rw-r--r--mpfr/add1.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/mpfr/add1.c b/mpfr/add1.c
new file mode 100644
index 000000000..2c2cd39f0
--- /dev/null
+++ b/mpfr/add1.c
@@ -0,0 +1,548 @@
+/* mpfr_add1 -- internal function to perform a "real" addition
+
+Copyright (C) 1999-2001 Free Software Foundation.
+Contributed by the Spaces project, INRIA Lorraine.
+
+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"
+
+/* signs of b and c are supposed equal,
+ diff_exp is the difference between the exponents of b and c,
+ which is supposed >= 0 */
+
+int
+mpfr_add1 (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c,
+ mp_rnd_t rnd_mode, mp_exp_unsigned_t diff_exp)
+{
+ mp_limb_t *ap, *bp, *cp;
+ mp_prec_t aq, bq, cq, aq2;
+ mp_size_t an, bn, cn;
+ mp_exp_t difw;
+ int sh, rb, fb, inex;
+ TMP_DECL(marker);
+
+ MPFR_ASSERTN(MPFR_IS_FP(b) && MPFR_NOTZERO(b));
+ MPFR_ASSERTN(MPFR_IS_FP(c) && MPFR_NOTZERO(c));
+
+ TMP_MARK(marker);
+ ap = MPFR_MANT(a);
+ bp = MPFR_MANT(b);
+ cp = MPFR_MANT(c);
+
+ if (ap == bp)
+ {
+ bp = (mp_ptr) TMP_ALLOC(MPFR_ABSSIZE(b) * BYTES_PER_MP_LIMB);
+ MPN_COPY (bp, ap, MPFR_ABSSIZE(b));
+ if (ap == cp)
+ { cp = bp; }
+ }
+ else if (ap == cp)
+ {
+ cp = (mp_ptr) TMP_ALLOC (MPFR_ABSSIZE(c) * BYTES_PER_MP_LIMB);
+ MPN_COPY(cp, ap, MPFR_ABSSIZE(c));
+ }
+
+ aq = MPFR_PREC(a);
+ bq = MPFR_PREC(b);
+ cq = MPFR_PREC(c);
+
+ an = (aq-1)/BITS_PER_MP_LIMB + 1; /* number of significant limbs of a */
+ aq2 = an * BITS_PER_MP_LIMB;
+ sh = aq2 - aq; /* non-significant bits in low limb */
+ bn = (bq-1)/BITS_PER_MP_LIMB + 1; /* number of significant limbs of b */
+ cn = (cq-1)/BITS_PER_MP_LIMB + 1; /* number of significant limbs of c */
+
+ MPFR_EXP(a) = MPFR_EXP(b);
+ MPFR_SET_SAME_SIGN(a, b);
+
+ /*
+ * 1. Compute the significant part A', the non-significant bits of A
+ * are taken into account.
+ *
+ * 2. Perform the rounding. At each iteration, we remember:
+ * _ r = rounding bit
+ * _ f = following bits (same value)
+ * where the result has the form: [number A]rfff...fff + a remaining
+ * value in the interval [0,2) ulp. We consider the most significant
+ * bits of the remaining value to update the result; a possible carry
+ * is immediately taken into account and A is updated accordingly. As
+ * soon as the bits f don't have the same value, A can be rounded.
+ * Variables:
+ * _ rb = rounding bit (0 or 1).
+ * _ fb = following bits (0 or 1), then sticky bit.
+ * If fb == 0, the only thing that can change is the sticky bit.
+ */
+
+ rb = fb = -1; /* means: not initialized */
+
+ if (aq2 <= diff_exp)
+ { /* c does not overlap with a' */
+ if (an > bn)
+ { /* a has more limbs than b */
+ /* copy b to the most significant limbs of a */
+ MPN_COPY(ap + (an - bn), bp, bn);
+ /* zero the least significant limbs of a */
+ MPN_ZERO(ap, an - bn);
+ }
+ else /* an <= bn */
+ {
+ /* copy the most significant limbs of b to a */
+ MPN_COPY(ap, bp + (bn - an), an);
+ }
+ }
+ else /* aq2 > diff_exp */
+ { /* c overlaps with a' */
+ mp_limb_t *a2p;
+ mp_limb_t cc;
+ mp_prec_t dif;
+ mp_size_t difn, k;
+ int shift;
+
+ /* copy c (shifted) into a */
+
+ dif = aq2 - diff_exp;
+ /* dif is the number of bits of c which overlap with a' */
+
+ difn = (dif-1)/BITS_PER_MP_LIMB + 1;
+ /* only the highest difn limbs from c have to be considered */
+ if (difn > cn)
+ {
+ /* c doesn't have enough limbs; take into account the virtual
+ zero limbs now by zeroing the least significant limbs of a' */
+ MPFR_ASSERTN(difn - cn <= an);
+ MPN_ZERO(ap, difn - cn);
+ difn = cn;
+ }
+
+ k = diff_exp / BITS_PER_MP_LIMB;
+
+ /* zero the most significant k limbs of a */
+ a2p = ap + (an - k);
+ MPN_ZERO(a2p, k);
+
+ shift = diff_exp % BITS_PER_MP_LIMB;
+
+ if (shift)
+ {
+ MPFR_ASSERTN(a2p - difn >= ap);
+ cc = mpn_rshift(a2p - difn, cp + (cn - difn), difn, shift);
+ if (a2p - difn > ap)
+ *(a2p - difn - 1) = cc;
+ }
+ else
+ MPN_COPY(a2p - difn, cp + (cn - difn), difn);
+
+ /* add b to a */
+
+ cc = an > bn
+ ? mpn_add_n(ap + (an - bn), ap + (an - bn), bp, bn)
+ : mpn_add_n(ap, ap, bp + (bn - an), an);
+
+ if (cc) /* carry */
+ {
+ mp_exp_t exp = MPFR_EXP(a);
+ if (exp == __mpfr_emax)
+ {
+ inex = mpfr_set_overflow(a, rnd_mode, MPFR_SIGN(a));
+ goto end_of_add;
+ }
+ MPFR_EXP(a)++;
+ rb = (ap[0] >> sh) & 1; /* LSB(a) --> rounding bit after the shift */
+ if (sh)
+ {
+ mp_limb_t mask, bb;
+
+ mask = (MP_LIMB_T_ONE << sh) - 1;
+ bb = ap[0] & mask;
+ ap[0] &= (~mask) << 1;
+ if (bb == 0)
+ fb = 0;
+ else if (bb == mask)
+ fb = 1;
+ }
+ mpn_rshift(ap, ap, an, 1);
+ ap[an-1] += MP_LIMB_T_HIGHBIT;
+ if (sh && fb < 0)
+ goto rounding;
+ } /* cc */
+ } /* aq2 > diff_exp */
+
+ /* non-significant bits of a */
+ if (rb < 0 && sh)
+ {
+ mp_limb_t mask, bb;
+
+ mask = (MP_LIMB_T_ONE << sh) - 1;
+ bb = ap[0] & mask;
+ ap[0] &= ~mask;
+ rb = bb >> (sh - 1);
+ if (sh > 1)
+ {
+ mask >>= 1;
+ bb &= mask;
+ if (bb == 0)
+ fb = 0;
+ else if (bb == mask)
+ fb = 1;
+ else
+ goto rounding;
+ }
+ }
+
+ /* determine rounding and sticky bits (and possible carry) */
+
+ difw = an - diff_exp / BITS_PER_MP_LIMB;
+ /* difw is the number of limbs from b (regarded as having an infinite
+ precision) that have already been combined with c; -n if the next
+ n limbs from b won't be combined with c. */
+
+ if (bn > an)
+ { /* there are still limbs from b that haven't been taken into account */
+ mp_size_t bk;
+
+ if (fb == 0 && difw <= 0)
+ {
+ fb = 1; /* c hasn't been taken into account ==> sticky bit != 0 */
+ goto rounding;
+ }
+
+ bk = bn - an; /* index of lowest considered limb from b, > 0 */
+ while (difw < 0)
+ { /* ulp(next limb from b) > msb(c) */
+ mp_limb_t bb;
+
+ bb = bp[--bk];
+
+ MPFR_ASSERTD(fb != 0);
+ if (fb > 0)
+ {
+ if (bb != MP_LIMB_T_MAX)
+ {
+ fb = 1; /* c hasn't been taken into account
+ ==> sticky bit != 0 */
+ goto rounding;
+ }
+ }
+ else /* fb not initialized yet */
+ {
+ if (rb < 0) /* rb not initialized yet */
+ {
+ rb = bb >> (BITS_PER_MP_LIMB - 1);
+ bb |= MP_LIMB_T_HIGHBIT;
+ }
+ fb = 1;
+ if (bb != MP_LIMB_T_MAX)
+ goto rounding;
+ }
+
+ if (bk == 0)
+ { /* b has entirely been read */
+ fb = 1; /* c hasn't been taken into account
+ ==> sticky bit != 0 */
+ goto rounding;
+ }
+
+ difw++;
+ } /* while */
+ MPFR_ASSERTN(bk > 0 && difw >= 0);
+
+ if (difw <= cn)
+ {
+ mp_size_t ck;
+ mp_limb_t cprev;
+ int difs;
+
+ ck = cn - difw;
+ difs = diff_exp % BITS_PER_MP_LIMB;
+
+ if (difs == 0 && ck == 0)
+ goto c_read;
+
+ cprev = ck == cn ? 0 : cp[ck];
+
+ if (fb < 0)
+ {
+ mp_limb_t bb, cc;
+
+ if (difs)
+ {
+ cc = cprev << (BITS_PER_MP_LIMB - difs);
+ if (--ck >= 0)
+ {
+ cprev = cp[ck];
+ cc += cprev >> difs;
+ }
+ }
+ else
+ cc = cp[--ck];
+
+ bb = bp[--bk] + cc;
+
+ if (bb < cc /* carry */
+ && (rb < 0 || (rb ^= 1) == 0)
+ && mpn_add_1(ap, ap, an, MP_LIMB_T_ONE << sh))
+ {
+ mp_exp_t exp = MPFR_EXP(a);
+ if (exp == __mpfr_emax)
+ {
+ inex = mpfr_set_overflow(a, rnd_mode, MPFR_SIGN(a));
+ goto end_of_add;
+ }
+ MPFR_EXP(a)++;
+ ap[an-1] = MP_LIMB_T_HIGHBIT;
+ rb = 0;
+ }
+
+ if (rb < 0) /* rb not initialized yet */
+ {
+ rb = bb >> (BITS_PER_MP_LIMB - 1);
+ bb <<= 1;
+ bb |= bb >> (BITS_PER_MP_LIMB - 1);
+ }
+
+ fb = bb != 0;
+ if (fb && bb != MP_LIMB_T_MAX)
+ goto rounding;
+ } /* fb < 0 */
+
+ while (bk > 0)
+ {
+ mp_limb_t bb, cc;
+
+ if (difs)
+ {
+ if (ck < 0)
+ goto c_read;
+ cc = cprev << (BITS_PER_MP_LIMB - difs);
+ if (--ck >= 0)
+ {
+ cprev = cp[ck];
+ cc += cprev >> difs;
+ }
+ }
+ else
+ {
+ if (ck == 0)
+ goto c_read;
+ cc = cp[--ck];
+ }
+
+ bb = bp[--bk] + cc;
+ if (bb < cc) /* carry */
+ {
+ fb ^= 1;
+ if (fb)
+ goto rounding;
+ rb ^= 1;
+ if (rb == 0 && mpn_add_1(ap, ap, an, MP_LIMB_T_ONE << sh))
+ {
+ mp_exp_t exp = MPFR_EXP(a);
+ if (exp == __mpfr_emax)
+ {
+ inex = mpfr_set_overflow(a, rnd_mode, MPFR_SIGN(a));
+ goto end_of_add;
+ }
+ MPFR_EXP(a)++;
+ ap[an-1] = MP_LIMB_T_HIGHBIT;
+ }
+ } /* bb < cc */
+
+ if (!fb && bb != 0)
+ {
+ fb = 1;
+ goto rounding;
+ }
+ if (fb && bb != MP_LIMB_T_MAX)
+ goto rounding;
+ } /* while */
+
+ /* b has entirely been read */
+
+ if (fb || ck < 0)
+ goto rounding;
+ if (difs && cprev << (BITS_PER_MP_LIMB - difs))
+ {
+ fb = 1;
+ goto rounding;
+ }
+ while (ck)
+ {
+ if (cp[--ck])
+ {
+ fb = 1;
+ goto rounding;
+ }
+ } /* while */
+ } /* difw <= cn */
+ else
+ { /* c has entirely been read */
+ c_read:
+ if (fb < 0) /* fb not initialized yet */
+ {
+ mp_limb_t bb;
+
+ MPFR_ASSERTN(bk > 0);
+ bb = bp[--bk];
+ if (rb < 0) /* rb not initialized yet */
+ {
+ rb = bb >> (BITS_PER_MP_LIMB - 1);
+ bb &= ~MP_LIMB_T_HIGHBIT;
+ }
+ fb = bb != 0;
+ } /* fb < 0 */
+ if (fb)
+ goto rounding;
+ while (bk)
+ {
+ if (bp[--bk])
+ {
+ fb = 1;
+ goto rounding;
+ }
+ } /* while */
+ } /* difw > cn */
+ } /* bn > an */
+ else if (fb != 1) /* if fb == 1, the sticky bit is 1 (no possible carry) */
+ { /* b has entirely been read */
+ if (difw > cn)
+ { /* c has entirely been read */
+ if (rb < 0)
+ rb = 0;
+ fb = 0;
+ }
+ else if (diff_exp > aq2)
+ { /* b is followed by at least a zero bit, then by c */
+ if (rb < 0)
+ rb = 0;
+ fb = 1;
+ }
+ else
+ {
+ mp_size_t ck;
+ int difs;
+
+ MPFR_ASSERTN(difw >= 0 && cn >= difw);
+ ck = cn - difw;
+ difs = diff_exp % BITS_PER_MP_LIMB;
+
+ if (difs == 0 && ck == 0)
+ { /* c has entirely been read */
+ if (rb < 0)
+ rb = 0;
+ fb = 0;
+ }
+ else
+ {
+ mp_limb_t cc;
+
+ cc = difs ? (/*MPFR_ASSERTN(ck < cn),*/
+ cp[ck] << (BITS_PER_MP_LIMB - difs)) : cp[--ck];
+ if (rb < 0)
+ {
+ rb = cc >> (BITS_PER_MP_LIMB - 1);
+ cc &= ~MP_LIMB_T_HIGHBIT;
+ }
+ while (cc == 0)
+ {
+ if (ck == 0)
+ {
+ fb = 0;
+ goto rounding;
+ }
+ cc = cp[--ck];
+ } /* while */
+ fb = 1;
+ }
+ }
+ } /* fb != 1 */
+
+ rounding:
+ switch (rnd_mode)
+ {
+ case GMP_RNDN:
+ if (fb == 0)
+ {
+ if (rb == 0)
+ {
+ inex = 0;
+ goto end_of_add;
+ }
+ /* round to even */
+ if (ap[0] & (MP_LIMB_T_ONE << sh))
+ goto rndn_away;
+ else
+ goto rndn_zero;
+ }
+ if (rb == 0)
+ {
+ rndn_zero:
+ inex = MPFR_ISNEG(a) ? 1 : -1;
+ goto end_of_add;
+ }
+ else
+ {
+ rndn_away:
+ inex = MPFR_ISNONNEG(a) ? 1 : -1;
+ goto add_one_ulp;
+ }
+
+ case GMP_RNDZ:
+ inex = rb || fb ? (MPFR_ISNEG(a) ? 1 : -1) : 0;
+ goto end_of_add;
+
+ case GMP_RNDU:
+ inex = rb || fb;
+ if (inex && MPFR_ISNONNEG(a))
+ goto add_one_ulp;
+ else
+ goto end_of_add;
+
+ case GMP_RNDD:
+ inex = - (rb || fb);
+ if (inex && MPFR_ISNEG(a))
+ goto add_one_ulp;
+ else
+ goto end_of_add;
+
+ default:
+ MPFR_ASSERTN(0);
+ inex = 0;
+ goto end_of_add;
+ }
+
+ add_one_ulp: /* add one unit in last place to a */
+ if (mpn_add_1(ap, ap, an, MP_LIMB_T_ONE << sh))
+ {
+ mp_exp_t exp = MPFR_EXP(a);
+ if (exp == __mpfr_emax)
+ inex = mpfr_set_overflow(a, rnd_mode, MPFR_SIGN(a));
+ else
+ {
+ MPFR_EXP(a)++;
+ ap[an-1] = MP_LIMB_T_HIGHBIT;
+ }
+ }
+
+ end_of_add:
+ TMP_FREE(marker);
+ return inex;
+}