macssh/gmp/mpfr/round.c

339 lines
10 KiB
C
Executable File

/* mpfr_round_raw2, mpfr_round_raw, mpfr_round, mpfr_can_round,
mpfr_can_round_raw -- various rounding functions
Copyright (C) 1999 PolKA project, Inria Lorraine and Loria
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 Library General Public License as published by
the Free Software Foundation; either version 2 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 Library General Public
License for more details.
You should have received a copy of the GNU Library 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 <stdio.h>
#include "gmp.h"
#include "gmp-impl.h"
#include "mpfr.h"
#ifdef Exp
#include "longlong.h"
#endif
/* returns 0 if round(sign*xp[0..xn-1], prec, rnd) =
round(sign*xp[0..xn-1], prec, GMP_RNDZ), 1 otherwise,
where sign=1 if neg=0, sign=-1 otherwise.
Does *not* modify anything.
*/
int
#if __STDC__
mpfr_round_raw2(mp_limb_t *xp, unsigned long xn,
char neg, char rnd, unsigned long prec)
#else
mpfr_round_raw2(xp, xn, neg, rnd, prec)
mp_limb_t *xp;
unsigned long xn;
char neg;
char rnd;
unsigned long prec;
#endif
{
unsigned long nw; long wd; char rw; short l; mp_limb_t mask;
nw = prec / BITS_PER_MP_LIMB; rw = prec & (BITS_PER_MP_LIMB - 1);
if (rw) nw++;
if (rnd==GMP_RNDZ || xn<nw || (rnd==GMP_RNDU && neg)
|| (rnd==GMP_RNDD && neg==0)) return 0;
mask = ~((((mp_limb_t)1)<<(BITS_PER_MP_LIMB - rw)) - 1);
switch (rnd)
{
case GMP_RNDU:
case GMP_RNDD:
if (xp[xn - nw] & ~mask) return 1;
for (l = nw + 1;l <= xn; l++)
if (xp[xn - l]) break;
return (l <= xn);
case GMP_RNDN:
/* First check if we are just halfway between two representable numbers */
wd = xn - nw;
if (!rw)
{
if (!wd) /* all bits are significative */ return 0;
wd--;
if (xp[wd] == ((mp_limb_t)1 << (BITS_PER_MP_LIMB - 1)))
{
do wd--; while (wd > 0 && !xp[wd]);
if (!wd) { return 1; } else return xp[xn - nw] & 1;
}
return xp[wd]>>(BITS_PER_MP_LIMB - 1);
}
else
if (rw + 1 < BITS_PER_MP_LIMB)
{
if ((xp[wd] & (~mask)) == (((mp_limb_t)1) << (BITS_PER_MP_LIMB - rw - 1)))
do { wd--; } while (wd >= 0 && !xp[wd]);
else return ((xp[wd]>>(BITS_PER_MP_LIMB - rw - 1)) & 1);
/* first limb was in the middle, and others down to wd+1 were 0 */
if (wd>=0) return 1;
else
return ((xp[xn - nw] & mask) >> (BITS_PER_MP_LIMB - rw)) & 1;
}
else
/* Modified PZ, 27 May 1999:
rw, i.e. the number of bits stored in xp[xn-nw], is
BITS_PER_MP_LIMB-1, i.e. there is exactly one non significant bit.
We are just halfway iff xp[wd] has its low significant bit
set and all limbs xp[0]...xp[wd-1] are zero */
{
if (xp[wd] & 1)
do wd--; while (wd >= 0 && !xp[wd]);
return ((wd<0) ? xp[xn-nw]>>1 : xp[xn-nw]) & 1;
}
default: return 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).
*/
int
#if __STDC__
mpfr_round_raw(mp_limb_t *y, mp_limb_t *xp, unsigned long xprec, char negative,
unsigned long yprec, char RND_MODE)
#else
mpfr_round_raw(y, xp, xprec, negative, yprec, RND_MODE)
mp_limb_t *y;
mp_limb_t *xp;
unsigned long xprec;
char negative;
unsigned long yprec;
char RND_MODE;
#endif
{
unsigned long nw, xsize; mp_limb_t mask;
char rw, xrw, carry = 0;
xsize = (xprec-1)/BITS_PER_MP_LIMB + 1;
xrw = xprec % BITS_PER_MP_LIMB; if (xrw == 0) { xrw = BITS_PER_MP_LIMB; }
#ifdef Exp
count_leading_zeros(flag, xp[xsize-1]);
yprec += flag;
#endif
nw = yprec / BITS_PER_MP_LIMB; rw = yprec & (BITS_PER_MP_LIMB - 1);
if (rw) nw++;
/* number of words needed to represent x */
mask = ~((((mp_limb_t)1)<<(BITS_PER_MP_LIMB - rw)) - (mp_limb_t)1);
/* precision is larger than the size of x. No rounding is necessary.
Just add zeroes at the end */
if (xsize < nw) {
MPN_COPY(y + nw - xsize, xp, xsize);
MPN_ZERO(y, nw - xsize); /* PZ 27 May 99 */
y[0] &= mask;
return 0;
}
if (mpfr_round_raw2(xp, xsize, negative, RND_MODE, yprec))
carry = mpn_add_1(y, xp + xsize - nw, nw,
((mp_limb_t)1) << (BITS_PER_MP_LIMB - rw));
else MPN_COPY(y, xp + xsize - nw, nw);
y[0] &= mask;
return carry;
}
void
#if __STDC__
mpfr_round(mpfr_t x, char RND_MODE, unsigned long prec)
#else
mpfr_round(x, RND_MODE, prec)
mpfr_t x;
char RND_MODE;
unsigned long prec;
#endif
{
mp_limb_t *tmp; int carry; unsigned long nw;
TMP_DECL(marker);
nw = prec / BITS_PER_MP_LIMB;
if (prec & (BITS_PER_MP_LIMB - 1)) nw++;
TMP_MARK(marker);
tmp = TMP_ALLOC (nw * BYTES_PER_MP_LIMB);
carry = mpfr_round_raw(tmp, MANT(x), PREC(x), (SIGN(x)<0), prec, RND_MODE);
if (carry)
{
mpn_rshift(tmp, tmp, nw, 1);
tmp [nw-1] |= (((mp_limb_t)1) << (BITS_PER_MP_LIMB - 1));
EXP(x)++;
}
if (SIGN(x)<0) { SIZE(x)=nw; CHANGE_SIGN(x); } else SIZE(x)=nw;
PREC(x) = prec;
MPN_COPY(MANT(x), tmp, nw);
TMP_FREE(marker);
}
/* hypotheses : BITS_PER_MP_LIMB est une puissance de 2
dans un test, la premiere partie du && est evaluee d'abord */
/* assuming b is an approximation of x in direction rnd1
with error at most 2^(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
#if __STDC__
mpfr_can_round(mpfr_t b, unsigned long err, unsigned char rnd1,
unsigned char rnd2, unsigned long prec)
#else
mpfr_can_round(b, err, rnd1, rnd2, prec)
mpfr_t b;
unsigned long err;
unsigned char rnd1;
unsigned char rnd2;
unsigned long prec;
#endif
{
return mpfr_can_round_raw(MANT(b), (PREC(b) - 1)/BITS_PER_MP_LIMB + 1,
SIGN(b), err, rnd1, rnd2, prec);
}
int
#if __STDC__
mpfr_can_round_raw(mp_limb_t *bp, unsigned long bn, int neg,
unsigned long err, unsigned char rnd1, unsigned char rnd2,
unsigned long prec)
#else
mpfr_can_round_raw(bp, bn, neg, err, rnd1, rnd2, prec)
mp_limb_t *bp;
unsigned long bn;
int neg;
unsigned long err;
unsigned char rnd1;
unsigned char rnd2;
unsigned long prec;
#endif
{
int k, k1, l, l1, tn; mp_limb_t cc, cc2, *tmp;
TMP_DECL(marker);
if (err<=prec) return 0;
neg = (neg > 0 ? 0 : 1);
/* warning: if k = m*BITS_PER_MP_LIMB, consider limb m-1 and not m */
k = (err-1)/BITS_PER_MP_LIMB;
l = err % BITS_PER_MP_LIMB; if (l) l = BITS_PER_MP_LIMB-l;
/* the error corresponds to bit l in limb k */
k1 = (prec-1)/BITS_PER_MP_LIMB;
l1 = prec%BITS_PER_MP_LIMB; if (l1) l1 = BITS_PER_MP_LIMB-l1;
/* the last significant bit is bit l1 in limb k1 */
/* don't need to consider the k1 most significant limbs */
k -= k1; bn -= k1; prec -= k1*BITS_PER_MP_LIMB; k1=0;
if (rnd1==GMP_RNDU) { if (neg) rnd1=GMP_RNDZ; }
if (rnd1==GMP_RNDD) { if (neg) rnd1=GMP_RNDU; else rnd1=GMP_RNDZ; }
/* in the sequel, RNDU = towards infinity, RNDZ = towards zero */
TMP_MARK(marker);
tn = bn;
k++; /* since we work with k+1 everywhere */
switch (rnd1) {
case GMP_RNDZ: /* b <= x <= b+2^(EXP(b)-err) */
tmp = TMP_ALLOC(tn*BYTES_PER_MP_LIMB);
cc = (bp[bn-1]>>l1) & 1;
cc ^= mpfr_round_raw2(bp, bn, neg, rnd2, prec);
/* now round b+2^(EXP(b)-err) */
cc2 = mpn_add_1(tmp+bn-k, bp+bn-k, k, (mp_limb_t)1<<l);
/* if carry, then all bits up to err were to 1, and we can round only
if cc==0 and mpfr_round_raw2 returns 0 below */
if (cc2 && cc) { TMP_FREE(marker); return 0; }
cc2 = (tmp[bn-1]>>l1) & 1; /* gives 0 when carry */
cc2 ^= mpfr_round_raw2(tmp, bn, neg, rnd2, prec);
TMP_FREE(marker);
return (cc == cc2);
case GMP_RNDU: /* b-2^(EXP(b)-err) <= x <= b */
tmp = TMP_ALLOC(tn*BYTES_PER_MP_LIMB);
/* first round b */
cc = (bp[bn-1]>>l1) & 1;
cc ^= mpfr_round_raw2(bp, bn, neg, rnd2, prec);
/* now round b-2^(EXP(b)-err) */
cc2 = mpn_sub_1(tmp+bn-k, bp+bn-k, k, (mp_limb_t)1<<l);
/* if borrow, then all bits up to err were to 0, and we can round only
if cc==0 and mpfr_round_raw2 returns 1 below */
if (cc2 && cc) { TMP_FREE(marker); return 0; }
cc2 = (tmp[bn-1]>>l1) & 1; /* gives 1 when carry */
cc2 ^= mpfr_round_raw2(tmp, bn, neg, rnd2, prec);
TMP_FREE(marker);
return (cc == cc2);
case GMP_RNDN: /* b-2^(EXP(b)-err-1) <= x <= b+2^(EXP(b)-err-1) */
if (l==0) tn++;
tmp = TMP_ALLOC(tn*BYTES_PER_MP_LIMB);
/* this case is the same than GMP_RNDZ, except we first have to
subtract 2^(EXP(b)-err-1) from b */
if (l) {
l--; /* tn=bn */
mpn_sub_1(tmp+tn-k, bp+bn-k, k, (mp_limb_t)1<<l);
}
else {
MPN_COPY(tmp+1, bp, bn); *tmp=0; /* extra limb to add or subtract 1 */
k++; l=BITS_PER_MP_LIMB-1;
mpn_sub_1(tmp+tn-k, tmp+tn-k, k, (mp_limb_t)1<<l);
}
/* round b-2^(EXP(b)-err-1) */
/* we can disregard borrow, since we start from tmp in 2nd case too */
cc = (tmp[tn-1]>>l1) & 1;
cc ^= mpfr_round_raw2(tmp, tn, neg, rnd2, prec);
if (l==BITS_PER_MP_LIMB-1) { l=0; k--; } else l++;
/* round b+2^(EXP(b)-err-1) = b-2^(EXP(b)-err-1) + 2^(EXP(b)-err) */
cc2 = mpn_add_1(tmp+tn-k, tmp+tn-k, k, (mp_limb_t)1<<l);
/* if carry, then all bits up to err were to 1, and we can round only
if cc==0 and mpfr_round_raw2 returns 0 below */
if (cc2 && cc) { TMP_FREE(marker); return 0; }
cc2 = (tmp[tn-1]>>l1) & 1; /* gives 0 when carry */
cc2 ^= mpfr_round_raw2(tmp, tn, neg, rnd2, prec);
TMP_FREE(marker);
return (cc == cc2);
}
return 0;
}