]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - mpfr/sub1.c
Imported gcc-4.4.3
[msp430-gcc.git] / mpfr / sub1.c
diff --git a/mpfr/sub1.c b/mpfr/sub1.c
new file mode 100644 (file)
index 0000000..68b5e01
--- /dev/null
@@ -0,0 +1,538 @@
+/* mpfr_sub1 -- internal function to perform a "real" subtraction
+
+Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+Contributed by the Arenaire and Cacao projects, INRIA.
+
+This file is part of the GNU MPFR Library.
+
+The GNU 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 GNU 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 GNU MPFR Library; see the file COPYING.LIB.  If not, write to
+the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+MA 02110-1301, USA. */
+
+#include "mpfr-impl.h"
+
+/* compute sign(b) * (|b| - |c|), with |b| > |c|, diff_exp = EXP(b) - EXP(c)
+   Returns 0 iff result is exact,
+   a negative value when the result is less than the exact value,
+   a positive value otherwise.
+*/
+
+int
+mpfr_sub1 (mpfr_ptr a, mpfr_srcptr b, mpfr_srcptr c, mp_rnd_t rnd_mode)
+{
+  int sign;
+  mp_exp_unsigned_t diff_exp;
+  mp_prec_t cancel, cancel1;
+  mp_size_t cancel2, an, bn, cn, cn0;
+  mp_limb_t *ap, *bp, *cp;
+  mp_limb_t carry, bb, cc, borrow = 0;
+  int inexact, shift_b, shift_c, is_exact = 1, down = 0, add_exp = 0;
+  int sh, k;
+  MPFR_TMP_DECL(marker);
+
+  MPFR_TMP_MARK(marker);
+  ap = MPFR_MANT(a);
+  an = MPFR_LIMB_SIZE(a);
+
+  sign = mpfr_cmp2 (b, c, &cancel);
+  if (MPFR_UNLIKELY(sign == 0))
+    {
+      if (rnd_mode == GMP_RNDD)
+        MPFR_SET_NEG (a);
+      else
+        MPFR_SET_POS (a);
+      MPFR_SET_ZERO (a);
+      MPFR_RET (0);
+    }
+
+  /*
+   * If subtraction: sign(a) = sign * sign(b)
+   * If addition: sign(a) = sign of the larger argument in absolute value.
+   *
+   * Both cases can be simplidied in:
+   * if (sign>0)
+   *    if addition: sign(a) = sign * sign(b) = sign(b)
+   *    if subtraction, b is greater, so sign(a) = sign(b)
+   * else
+   *    if subtraction, sign(a) = - sign(b)
+   *    if addition, sign(a) = sign(c) (since c is greater)
+   *      But if it is an addition, sign(b) and sign(c) are opposed!
+   *      So sign(a) = - sign(b)
+   */
+
+  if (sign < 0) /* swap b and c so that |b| > |c| */
+    {
+      mpfr_srcptr t;
+      MPFR_SET_OPPOSITE_SIGN (a,b);
+      t = b; b = c; c = t;
+    }
+  else
+    MPFR_SET_SAME_SIGN (a,b);
+
+  /* Check if c is too small.
+     A more precise test is to replace 2 by
+      (rnd == GMP_RNDN) + mpfr_power2_raw (b)
+      but it is more expensive and not very useful */
+  if (MPFR_UNLIKELY (MPFR_GET_EXP (c) <= MPFR_GET_EXP (b)
+                     - (mp_exp_t) MAX (MPFR_PREC (a), MPFR_PREC (b)) - 2))
+    {
+      /* Remember, we can't have an exact result! */
+      /*   A.AAAAAAAAAAAAAAAAA
+         = B.BBBBBBBBBBBBBBB
+          -                     C.CCCCCCCCCCCCC */
+      /* A = S*ABS(B) +/- ulp(a) */
+      MPFR_SET_EXP (a, MPFR_GET_EXP (b));
+      MPFR_RNDRAW_EVEN (inexact, a, MPFR_MANT (b), MPFR_PREC (b),
+                        rnd_mode, MPFR_SIGN (a),
+                        if (MPFR_UNLIKELY ( ++MPFR_EXP (a) > __gmpfr_emax))
+                        inexact = mpfr_overflow (a, rnd_mode, MPFR_SIGN (a)));
+      /* inexact = mpfr_set4 (a, b, rnd_mode, MPFR_SIGN (a));  */
+      if (inexact == 0)
+        {
+          /* a = b (Exact)
+             But we know it isn't (Since we have to remove `c')
+             So if we round to Zero, we have to remove one ulp.
+             Otherwise the result is correctly rounded. */
+          if (MPFR_IS_LIKE_RNDZ (rnd_mode, MPFR_IS_NEG (a)))
+            {
+              mpfr_nexttozero (a);
+              MPFR_RET (- MPFR_INT_SIGN (a));
+            }
+          MPFR_RET (MPFR_INT_SIGN (a));
+        }
+      else
+        {
+          /*   A.AAAAAAAAAAAAAA
+             = B.BBBBBBBBBBBBBBB
+              -                   C.CCCCCCCCCCCCC */
+          /* It isn't exact so Prec(b) > Prec(a) and the last
+             Prec(b)-Prec(a) bits of `b' are not zeros.
+             Which means that removing c from b can't generate a carry
+             execpt in case of even rounding.
+             In all other case the result and the inexact flag should be
+             correct (We can't have an exact result).
+             In case of EVEN rounding:
+               1.BBBBBBBBBBBBBx10
+             -                     1.CCCCCCCCCCCC
+             = 1.BBBBBBBBBBBBBx01  Rounded to Prec(b)
+             = 1.BBBBBBBBBBBBBx    Nearest / Rounded to Prec(a)
+             Set gives:
+               1.BBBBBBBBBBBBB0   if inexact == EVEN_INEX  (x == 0)
+               1.BBBBBBBBBBBBB1+1 if inexact == -EVEN_INEX (x == 1)
+             which means we get a wrong rounded result if x==1,
+             i.e. inexact= MPFR_EVEN_INEX */
+          if (MPFR_UNLIKELY (inexact == MPFR_EVEN_INEX*MPFR_INT_SIGN (a)))
+            {
+              mpfr_nexttozero (a);
+              inexact = -MPFR_INT_SIGN (a);
+            }
+          MPFR_RET (inexact);
+        }
+    }
+
+  diff_exp = (mp_exp_unsigned_t) MPFR_GET_EXP (b) - MPFR_GET_EXP (c);
+
+  /* reserve a space to store b aligned with the result, i.e. shifted by
+     (-cancel) % BITS_PER_MP_LIMB to the right */
+  bn      = MPFR_LIMB_SIZE (b);
+  MPFR_UNSIGNED_MINUS_MODULO (shift_b, cancel);
+  cancel1 = (cancel + shift_b) / BITS_PER_MP_LIMB;
+
+  /* the high cancel1 limbs from b should not be taken into account */
+  if (MPFR_UNLIKELY (shift_b == 0))
+    {
+      bp = MPFR_MANT(b); /* no need of an extra space */
+      /* Ensure ap != bp */
+      if (MPFR_UNLIKELY (ap == bp))
+        {
+          bp = (mp_ptr) MPFR_TMP_ALLOC(bn * BYTES_PER_MP_LIMB);
+          MPN_COPY (bp, ap, bn);
+        }
+    }
+  else
+    {
+      bp = (mp_ptr) MPFR_TMP_ALLOC ((bn + 1) * BYTES_PER_MP_LIMB);
+      bp[0] = mpn_rshift (bp + 1, MPFR_MANT(b), bn++, shift_b);
+    }
+
+  /* reserve a space to store c aligned with the result, i.e. shifted by
+      (diff_exp-cancel) % BITS_PER_MP_LIMB to the right */
+  cn      = MPFR_LIMB_SIZE(c);
+  if ((UINT_MAX % BITS_PER_MP_LIMB) == (BITS_PER_MP_LIMB-1)
+      && ((-(unsigned) 1)%BITS_PER_MP_LIMB > 0))
+    shift_c = (diff_exp - cancel) % BITS_PER_MP_LIMB;
+  else
+    {
+      shift_c = diff_exp - (cancel % BITS_PER_MP_LIMB);
+      shift_c = (shift_c + BITS_PER_MP_LIMB) % BITS_PER_MP_LIMB;
+    }
+  MPFR_ASSERTD( shift_c >= 0 && shift_c < BITS_PER_MP_LIMB);
+
+  if (MPFR_UNLIKELY(shift_c == 0))
+    {
+       cp = MPFR_MANT(c);
+      /* Ensure ap != cp */
+      if (ap == cp)
+        {
+          cp = (mp_ptr) MPFR_TMP_ALLOC (cn * BYTES_PER_MP_LIMB);
+          MPN_COPY(cp, ap, cn);
+        }
+    }
+ else
+    {
+      cp = (mp_ptr) MPFR_TMP_ALLOC ((cn + 1) * BYTES_PER_MP_LIMB);
+      cp[0] = mpn_rshift (cp + 1, MPFR_MANT(c), cn++, shift_c);
+    }
+
+#ifdef DEBUG
+  printf ("shift_b=%d shift_c=%d diffexp=%lu\n", shift_b, shift_c,
+          (unsigned long) diff_exp);
+#endif
+
+  MPFR_ASSERTD (ap != cp);
+  MPFR_ASSERTD (bp != cp);
+
+  /* here we have shift_c = (diff_exp - cancel) % BITS_PER_MP_LIMB,
+        0 <= shift_c < BITS_PER_MP_LIMB
+     thus we want cancel2 = ceil((cancel - diff_exp) / BITS_PER_MP_LIMB) */
+
+  cancel2 = (long int) (cancel - (diff_exp - shift_c)) / BITS_PER_MP_LIMB;
+  /* the high cancel2 limbs from b should not be taken into account */
+#ifdef DEBUG
+  printf ("cancel=%lu cancel1=%lu cancel2=%ld\n",
+          (unsigned long) cancel, (unsigned long) cancel1, (long) cancel2);
+#endif
+
+  /*               ap[an-1]        ap[0]
+             <----------------+-----------|---->
+             <----------PREC(a)----------><-sh->
+ cancel1
+ limbs        bp[bn-cancel1-1]
+ <--...-----><----------------+-----------+----------->
+  cancel2
+  limbs       cp[cn-cancel2-1]                                    cancel2 >= 0
+    <--...--><----------------+----------------+---------------->
+                (-cancel2)                                        cancel2 < 0
+                   limbs      <----------------+---------------->
+  */
+
+  /* first part: put in ap[0..an-1] the value of high(b) - high(c),
+     where high(b) consists of the high an+cancel1 limbs of b,
+     and high(c) consists of the high an+cancel2 limbs of c.
+   */
+
+  /* copy high(b) into a */
+  if (MPFR_LIKELY(an + (mp_size_t) cancel1 <= bn))
+    /* a: <----------------+-----------|---->
+       b: <-----------------------------------------> */
+      MPN_COPY (ap, bp + bn - (an + cancel1), an);
+  else
+    /* a: <----------------+-----------|---->
+       b: <-------------------------> */
+    if ((mp_size_t) cancel1 < bn) /* otherwise b does not overlap with a */
+      {
+        MPN_ZERO (ap, an + cancel1 - bn);
+        MPN_COPY (ap + an + cancel1 - bn, bp, bn - cancel1);
+      }
+    else
+      MPN_ZERO (ap, an);
+
+#ifdef DEBUG
+  printf("after copying high(b), a="); mpfr_print_binary(a); putchar('\n');
+#endif
+
+  /* subtract high(c) */
+  if (MPFR_LIKELY(an + cancel2 > 0)) /* otherwise c does not overlap with a */
+    {
+      mp_limb_t *ap2;
+
+      if (cancel2 >= 0)
+        {
+          if (an + cancel2 <= cn)
+            /* a: <----------------------------->
+               c: <-----------------------------------------> */
+            mpn_sub_n (ap, ap, cp + cn - (an + cancel2), an);
+          else
+            /* a: <---------------------------->
+               c: <-------------------------> */
+            {
+              ap2 = ap + an + cancel2 - cn;
+              if (cn > cancel2)
+                mpn_sub_n (ap2, ap2, cp, cn - cancel2);
+            }
+        }
+      else /* cancel2 < 0 */
+        {
+          if (an + cancel2 <= cn)
+            /* a: <----------------------------->
+               c: <-----------------------------> */
+            borrow = mpn_sub_n (ap, ap, cp + cn - (an + cancel2),
+                                an + cancel2);
+          else
+            /* a: <---------------------------->
+               c: <----------------> */
+            {
+              ap2 = ap + an + cancel2 - cn;
+              borrow = mpn_sub_n (ap2, ap2, cp, cn);
+            }
+          ap2 = ap + an + cancel2;
+          mpn_sub_1 (ap2, ap2, -cancel2, borrow);
+        }
+    }
+
+#ifdef DEBUG
+  printf("after subtracting high(c), a=");
+  mpfr_print_binary(a);
+  putchar('\n');
+#endif
+
+  /* now perform rounding */
+  sh = (mp_prec_t) an * BITS_PER_MP_LIMB - MPFR_PREC(a);
+  /* last unused bits from a */
+  carry = ap[0] & MPFR_LIMB_MASK (sh);
+  ap[0] -= carry;
+
+  if (MPFR_LIKELY(rnd_mode == GMP_RNDN))
+    {
+      if (MPFR_LIKELY(sh))
+        {
+          is_exact = (carry == 0);
+          /* can decide except when carry = 2^(sh-1) [middle]
+             or carry = 0 [truncate, but cannot decide inexact flag] */
+          down = (carry < (MPFR_LIMB_ONE << (sh - 1)));
+          if (carry > (MPFR_LIMB_ONE << (sh - 1)))
+            goto add_one_ulp;
+          else if ((0 < carry) && down)
+            {
+              inexact = -1; /* result if smaller than exact value */
+              goto truncate;
+            }
+        }
+    }
+  else /* directed rounding: set rnd_mode to RNDZ iff towards zero */
+    {
+      if (MPFR_IS_RNDUTEST_OR_RNDDNOTTEST(rnd_mode, MPFR_IS_NEG(a)))
+        rnd_mode = GMP_RNDZ;
+
+      if (carry)
+        {
+          if (rnd_mode == GMP_RNDZ)
+            {
+              inexact = -1;
+              goto truncate;
+            }
+          else /* round away */
+            goto add_one_ulp;
+        }
+    }
+
+  /* we have to consider the low (bn - (an+cancel1)) limbs from b,
+     and the (cn - (an+cancel2)) limbs from c. */
+  bn -= an + cancel1;
+  cn0 = cn;
+  cn -= (long int) an + cancel2;
+
+#ifdef DEBUG
+  printf ("last %d bits from a are %lu, bn=%ld, cn=%ld\n",
+          sh, (unsigned long) carry, (long) bn, (long) cn);
+#endif
+
+  for (k = 0; (bn > 0) || (cn > 0); k = 1)
+    {
+      /* get next limbs */
+      bb = (bn > 0) ? bp[--bn] : 0;
+      if ((cn > 0) && (cn-- <= cn0))
+        cc = cp[cn];
+      else
+        cc = 0;
+
+      /* down is set when low(b) < low(c) */
+      if (down == 0)
+        down = (bb < cc);
+
+      /* the case rounding to nearest with sh=0 is special since one couldn't
+         subtract above 1/2 ulp in the trailing limb of the result */
+      if ((rnd_mode == GMP_RNDN) && sh == 0 && k == 0)
+        {
+          mp_limb_t half = MPFR_LIMB_HIGHBIT;
+
+          is_exact = (bb == cc);
+
+          /* add one ulp if bb > cc + half
+             truncate if cc - half < bb < cc + half
+             sub one ulp if bb < cc - half
+          */
+
+          if (down)
+            {
+              if (cc >= half)
+                cc -= half;
+              else
+                bb += half;
+            }
+          else /* bb >= cc */
+            {
+              if (cc < half)
+                cc += half;
+              else
+                bb -= half;
+            }
+        }
+
+#ifdef DEBUG
+      printf ("    bb=%lu cc=%lu down=%d is_exact=%d\n",
+              (unsigned long) bb, (unsigned long) cc, down, is_exact);
+#endif
+      if (bb < cc)
+        {
+          if (rnd_mode == GMP_RNDZ)
+            goto sub_one_ulp;
+          else if (rnd_mode != GMP_RNDN) /* round away */
+            {
+              inexact = 1;
+              goto truncate;
+            }
+          else /* round to nearest: special case here since for sh=k=0
+                  bb = bb0 - MPFR_LIMB_HIGHBIT */
+            {
+              if (is_exact && sh == 0)
+                {
+                  /* For k=0 we can't decide exactness since it may depend
+                     from low order bits.
+                     For k=1, the first low limbs matched: low(b)-low(c)<0. */
+                  if (k)
+                    {
+                      inexact = 1;
+                      goto truncate;
+                    }
+                }
+              else if (down && sh == 0)
+                goto sub_one_ulp;
+              else
+                {
+                  inexact = (is_exact) ? 1 : -1;
+                  goto truncate;
+                }
+            }
+        }
+      else if (bb > cc)
+        {
+          if (rnd_mode == GMP_RNDZ)
+            {
+              inexact = -1;
+              goto truncate;
+            }
+          else if (rnd_mode != GMP_RNDN) /* round away */
+            goto add_one_ulp;
+          else /* round to nearest */
+            {
+              if (is_exact)
+                {
+                  inexact = -1;
+                  goto truncate;
+                }
+              else if (down)
+                {
+                  inexact = 1;
+                  goto truncate;
+                }
+              else
+                goto add_one_ulp;
+            }
+        }
+    }
+
+  if ((rnd_mode == GMP_RNDN) && !is_exact)
+    {
+      /* even rounding rule */
+      if ((ap[0] >> sh) & 1)
+        {
+          if (down)
+            goto sub_one_ulp;
+          else
+            goto add_one_ulp;
+        }
+      else
+        inexact = (down) ? 1 : -1;
+    }
+  else
+    inexact = 0;
+  goto truncate;
+
+ sub_one_ulp: /* sub one unit in last place to a */
+  mpn_sub_1 (ap, ap, an, MPFR_LIMB_ONE << sh);
+  inexact = -1;
+  goto end_of_sub;
+
+ add_one_ulp: /* add one unit in last place to a */
+  if (MPFR_UNLIKELY(mpn_add_1 (ap, ap, an, MPFR_LIMB_ONE << sh)))
+    /* result is a power of 2: 11111111111111 + 1 = 1000000000000000 */
+    {
+      ap[an-1] = MPFR_LIMB_HIGHBIT;
+      add_exp = 1;
+    }
+  inexact = 1; /* result larger than exact value */
+
+ truncate:
+  if (MPFR_UNLIKELY((ap[an-1] >> (BITS_PER_MP_LIMB - 1)) == 0))
+    /* case 1 - epsilon */
+    {
+      ap[an-1] = MPFR_LIMB_HIGHBIT;
+      add_exp = 1;
+    }
+
+ end_of_sub:
+  /* we have to set MPFR_EXP(a) to MPFR_EXP(b) - cancel + add_exp, taking
+     care of underflows/overflows in that computation, and of the allowed
+     exponent range */
+  if (MPFR_LIKELY(cancel))
+    {
+      mp_exp_t exp_a;
+
+      cancel -= add_exp; /* still valid as unsigned long */
+      exp_a = MPFR_GET_EXP (b) - cancel;
+      if (MPFR_UNLIKELY(exp_a < __gmpfr_emin))
+        {
+          MPFR_TMP_FREE(marker);
+          if (rnd_mode == GMP_RNDN &&
+              (exp_a < __gmpfr_emin - 1 ||
+               (inexact >= 0 && mpfr_powerof2_raw (a))))
+            rnd_mode = GMP_RNDZ;
+          return mpfr_underflow (a, rnd_mode, MPFR_SIGN(a));
+        }
+      MPFR_SET_EXP (a, exp_a);
+    }
+  else /* cancel = 0: MPFR_EXP(a) <- MPFR_EXP(b) + add_exp */
+    {
+      /* in case cancel = 0, add_exp can still be 1, in case b is just
+         below a power of two, c is very small, prec(a) < prec(b),
+         and rnd=away or nearest */
+      mp_exp_t exp_b;
+
+      exp_b = MPFR_GET_EXP (b);
+      if (MPFR_UNLIKELY(add_exp && exp_b == __gmpfr_emax))
+        {
+          MPFR_TMP_FREE(marker);
+          return mpfr_overflow (a, rnd_mode, MPFR_SIGN(a));
+        }
+      MPFR_SET_EXP (a, exp_b + add_exp);
+    }
+  MPFR_TMP_FREE(marker);
+#ifdef DEBUG
+  printf ("result is a="); mpfr_print_binary(a); putchar('\n');
+#endif
+  /* check that result is msb-normalized */
+  MPFR_ASSERTD(ap[an-1] > ~ap[an-1]);
+  MPFR_RET (inexact * MPFR_INT_SIGN (a));
+}