aboutsummaryrefslogtreecommitdiff
path: root/vendor/gmp-6.3.0/mpf/sub.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gmp-6.3.0/mpf/sub.c')
-rw-r--r--vendor/gmp-6.3.0/mpf/sub.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/vendor/gmp-6.3.0/mpf/sub.c b/vendor/gmp-6.3.0/mpf/sub.c
new file mode 100644
index 0000000..56f26f6
--- /dev/null
+++ b/vendor/gmp-6.3.0/mpf/sub.c
@@ -0,0 +1,395 @@
+/* mpf_sub -- Subtract two floats.
+
+Copyright 1993-1996, 1999-2002, 2004, 2005, 2011, 2014 Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library is free software; you can redistribute it and/or modify
+it under the terms of either:
+
+ * the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+or
+
+ * the GNU General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any
+ later version.
+
+or both in parallel, as here.
+
+The GNU MP 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 General Public License
+for more details.
+
+You should have received copies of the GNU General Public License and the
+GNU Lesser General Public License along with the GNU MP Library. If not,
+see https://www.gnu.org/licenses/. */
+
+#include "gmp-impl.h"
+
+void
+mpf_sub (mpf_ptr r, mpf_srcptr u, mpf_srcptr v)
+{
+ mp_srcptr up, vp;
+ mp_ptr rp, tp;
+ mp_size_t usize, vsize, rsize;
+ mp_size_t prec;
+ mp_exp_t exp;
+ mp_size_t ediff;
+ int negate;
+ TMP_DECL;
+
+ usize = SIZ (u);
+ vsize = SIZ (v);
+
+ /* Handle special cases that don't work in generic code below. */
+ if (usize == 0)
+ {
+ mpf_neg (r, v);
+ return;
+ }
+ if (vsize == 0)
+ {
+ if (r != u)
+ mpf_set (r, u);
+ return;
+ }
+
+ /* If signs of U and V are different, perform addition. */
+ if ((usize ^ vsize) < 0)
+ {
+ __mpf_struct v_negated;
+ v_negated._mp_size = -vsize;
+ v_negated._mp_exp = EXP (v);
+ v_negated._mp_d = PTR (v);
+ mpf_add (r, u, &v_negated);
+ return;
+ }
+
+ TMP_MARK;
+
+ /* Signs are now known to be the same. */
+ negate = usize < 0;
+
+ /* Make U be the operand with the largest exponent. */
+ if (EXP (u) < EXP (v))
+ {
+ mpf_srcptr t;
+ t = u; u = v; v = t;
+ negate ^= 1;
+ usize = SIZ (u);
+ vsize = SIZ (v);
+ }
+
+ usize = ABS (usize);
+ vsize = ABS (vsize);
+ up = PTR (u);
+ vp = PTR (v);
+ rp = PTR (r);
+ prec = PREC (r) + 1;
+ exp = EXP (u);
+ ediff = exp - EXP (v);
+
+ /* If ediff is 0 or 1, we might have a situation where the operands are
+ extremely close. We need to scan the operands from the most significant
+ end ignore the initial parts that are equal. */
+ if (ediff <= 1)
+ {
+ if (ediff == 0)
+ {
+ /* Skip leading limbs in U and V that are equal. */
+ /* This loop normally exits immediately. Optimize for that. */
+ while (up[usize - 1] == vp[vsize - 1])
+ {
+ usize--;
+ vsize--;
+ exp--;
+
+ if (usize == 0)
+ {
+ /* u cancels high limbs of v, result is rest of v */
+ negate ^= 1;
+ cancellation:
+ /* strip high zeros before truncating to prec */
+ while (vsize != 0 && vp[vsize - 1] == 0)
+ {
+ vsize--;
+ exp--;
+ }
+ if (vsize > prec)
+ {
+ vp += vsize - prec;
+ vsize = prec;
+ }
+ MPN_COPY_INCR (rp, vp, vsize);
+ rsize = vsize;
+ goto done;
+ }
+ if (vsize == 0)
+ {
+ vp = up;
+ vsize = usize;
+ goto cancellation;
+ }
+ }
+
+ if (up[usize - 1] < vp[vsize - 1])
+ {
+ /* For simplicity, swap U and V. Note that since the loop above
+ wouldn't have exited unless up[usize - 1] and vp[vsize - 1]
+ were non-equal, this if-statement catches all cases where U
+ is smaller than V. */
+ MPN_SRCPTR_SWAP (up,usize, vp,vsize);
+ negate ^= 1;
+ /* negating ediff not necessary since it is 0. */
+ }
+
+ /* Check for
+ x+1 00000000 ...
+ x ffffffff ... */
+ if (up[usize - 1] != vp[vsize - 1] + 1)
+ goto general_case;
+ usize--;
+ vsize--;
+ exp--;
+ }
+ else /* ediff == 1 */
+ {
+ /* Check for
+ 1 00000000 ...
+ 0 ffffffff ... */
+
+ if (up[usize - 1] != 1 || vp[vsize - 1] != GMP_NUMB_MAX
+ || (usize >= 2 && up[usize - 2] != 0))
+ goto general_case;
+
+ usize--;
+ exp--;
+ }
+
+ /* Skip sequences of 00000000/ffffffff */
+ while (vsize != 0 && usize != 0 && up[usize - 1] == 0
+ && vp[vsize - 1] == GMP_NUMB_MAX)
+ {
+ usize--;
+ vsize--;
+ exp--;
+ }
+
+ if (usize == 0)
+ {
+ while (vsize != 0 && vp[vsize - 1] == GMP_NUMB_MAX)
+ {
+ vsize--;
+ exp--;
+ }
+ }
+ else if (usize > prec - 1)
+ {
+ up += usize - (prec - 1);
+ usize = prec - 1;
+ }
+ if (vsize > prec - 1)
+ {
+ vp += vsize - (prec - 1);
+ vsize = prec - 1;
+ }
+
+ tp = TMP_ALLOC_LIMBS (prec);
+ {
+ mp_limb_t cy_limb;
+ if (vsize == 0)
+ {
+ MPN_COPY (tp, up, usize);
+ tp[usize] = 1;
+ rsize = usize + 1;
+ exp++;
+ goto normalized;
+ }
+ if (usize == 0)
+ {
+ cy_limb = mpn_neg (tp, vp, vsize);
+ rsize = vsize;
+ }
+ else if (usize >= vsize)
+ {
+ /* uuuu */
+ /* vv */
+ mp_size_t size;
+ size = usize - vsize;
+ MPN_COPY (tp, up, size);
+ cy_limb = mpn_sub_n (tp + size, up + size, vp, vsize);
+ rsize = usize;
+ }
+ else /* (usize < vsize) */
+ {
+ /* uuuu */
+ /* vvvvvvv */
+ mp_size_t size;
+ size = vsize - usize;
+ cy_limb = mpn_neg (tp, vp, size);
+ cy_limb = mpn_sub_nc (tp + size, up, vp + size, usize, cy_limb);
+ rsize = vsize;
+ }
+ if (cy_limb == 0)
+ {
+ tp[rsize] = 1;
+ rsize++;
+ exp++;
+ goto normalized;
+ }
+ goto normalize;
+ }
+ }
+
+general_case:
+ /* If U extends beyond PREC, ignore the part that does. */
+ if (usize > prec)
+ {
+ up += usize - prec;
+ usize = prec;
+ }
+
+ /* If V extends beyond PREC, ignore the part that does.
+ Note that this may make vsize negative. */
+ if (vsize + ediff > prec)
+ {
+ vp += vsize + ediff - prec;
+ vsize = prec - ediff;
+ }
+
+ if (ediff >= prec)
+ {
+ /* V completely cancelled. */
+ if (rp != up)
+ MPN_COPY (rp, up, usize);
+ rsize = usize;
+ }
+ else
+ {
+ /* Allocate temp space for the result. Allocate
+ just vsize + ediff later??? */
+ tp = TMP_ALLOC_LIMBS (prec);
+
+ /* Locate the least significant non-zero limb in (the needed
+ parts of) U and V, to simplify the code below. */
+ for (;;)
+ {
+ if (vsize == 0)
+ {
+ MPN_COPY (rp, up, usize);
+ rsize = usize;
+ goto done;
+ }
+ if (vp[0] != 0)
+ break;
+ vp++, vsize--;
+ }
+ for (;;)
+ {
+ if (usize == 0)
+ {
+ MPN_COPY (rp, vp, vsize);
+ rsize = vsize;
+ negate ^= 1;
+ goto done;
+ }
+ if (up[0] != 0)
+ break;
+ up++, usize--;
+ }
+
+ /* uuuu | uuuu | uuuu | uuuu | uuuu */
+ /* vvvvvvv | vv | vvvvv | v | vv */
+
+ if (usize > ediff)
+ {
+ /* U and V partially overlaps. */
+ if (ediff == 0)
+ {
+ /* Have to compare the leading limbs of u and v
+ to determine whether to compute u - v or v - u. */
+ if (usize >= vsize)
+ {
+ /* uuuu */
+ /* vv */
+ mp_size_t size;
+ size = usize - vsize;
+ MPN_COPY (tp, up, size);
+ mpn_sub_n (tp + size, up + size, vp, vsize);
+ rsize = usize;
+ }
+ else /* (usize < vsize) */
+ {
+ /* uuuu */
+ /* vvvvvvv */
+ mp_size_t size;
+ size = vsize - usize;
+ ASSERT_CARRY (mpn_neg (tp, vp, size));
+ mpn_sub_nc (tp + size, up, vp + size, usize, CNST_LIMB (1));
+ rsize = vsize;
+ }
+ }
+ else
+ {
+ if (vsize + ediff <= usize)
+ {
+ /* uuuu */
+ /* v */
+ mp_size_t size;
+ size = usize - ediff - vsize;
+ MPN_COPY (tp, up, size);
+ mpn_sub (tp + size, up + size, usize - size, vp, vsize);
+ rsize = usize;
+ }
+ else
+ {
+ /* uuuu */
+ /* vvvvv */
+ mp_size_t size;
+ rsize = vsize + ediff;
+ size = rsize - usize;
+ ASSERT_CARRY (mpn_neg (tp, vp, size));
+ mpn_sub (tp + size, up, usize, vp + size, usize - ediff);
+ /* Should we use sub_nc then sub_1? */
+ MPN_DECR_U (tp + size, usize, CNST_LIMB (1));
+ }
+ }
+ }
+ else
+ {
+ /* uuuu */
+ /* vv */
+ mp_size_t size, i;
+ size = vsize + ediff - usize;
+ ASSERT_CARRY (mpn_neg (tp, vp, vsize));
+ for (i = vsize; i < size; i++)
+ tp[i] = GMP_NUMB_MAX;
+ mpn_sub_1 (tp + size, up, usize, (mp_limb_t) 1);
+ rsize = size + usize;
+ }
+
+ normalize:
+ /* Full normalize. Optimize later. */
+ while (rsize != 0 && tp[rsize - 1] == 0)
+ {
+ rsize--;
+ exp--;
+ }
+ normalized:
+ MPN_COPY (rp, tp, rsize);
+ }
+
+ done:
+ TMP_FREE;
+ if (rsize == 0) {
+ SIZ (r) = 0;
+ EXP (r) = 0;
+ } else {
+ SIZ (r) = negate ? -rsize : rsize;
+ EXP (r) = exp;
+ }
+}