blob: 68b5e014a74cce40bab52e90a3ac69e6c6ed5026 [file] [log] [blame]
/* 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));
}