mirror of https://github.com/macssh/macssh.git
208 lines
6.5 KiB
C
208 lines
6.5 KiB
C
|
/* mpfr_get_str -- output a floating-point number to a string
|
||
|
|
||
|
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 <math.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "gmp.h"
|
||
|
#include "gmp-impl.h"
|
||
|
#include "longlong.h"
|
||
|
#include "mpfr.h"
|
||
|
|
||
|
/*
|
||
|
Convert op to a string in base 'base' with 'n' digits and writes the
|
||
|
mantissa in 'str', the exponent in 'expptr'.
|
||
|
The result is rounded wrt 'rnd_mode'.
|
||
|
|
||
|
For op = 3.1416 we get str = "31416" and expptr=1.
|
||
|
*/
|
||
|
#if __STDC__
|
||
|
char *mpfr_get_str(char *str, mp_exp_t *expptr, int base, size_t n,
|
||
|
mpfr_srcptr op, unsigned char rnd_mode)
|
||
|
#else
|
||
|
char *mpfr_get_str(str, expptr, base, n, op, rnd_mode)
|
||
|
char *str;
|
||
|
mp_exp_t *expptr;
|
||
|
int base;
|
||
|
size_t n;
|
||
|
mpfr_srcptr op;
|
||
|
unsigned char rnd_mode;
|
||
|
#endif
|
||
|
{
|
||
|
double d; long e, q, div, p, err, prec, sh; mpfr_t a, b; mpz_t bz;
|
||
|
char *str0; unsigned char rnd1; int f, pow2, ok=0, neg;
|
||
|
|
||
|
if (base<2 || 36<base) {
|
||
|
fprintf(stderr, "Error: too small or too large base in mpfr_get_str: %d\n",
|
||
|
base);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
neg = (SIGN(op)<0) ? 1 : 0;
|
||
|
|
||
|
if (!NOTZERO(op)) {
|
||
|
if (str==NULL) str0=str=(*_mp_allocate_func)(neg + n + 2);
|
||
|
if (SIGN(op)<0) *str++ = '-';
|
||
|
for (f=0;f<n;f++) *str++ = '0';
|
||
|
*expptr = 1;
|
||
|
return str0;
|
||
|
}
|
||
|
|
||
|
count_leading_zeros(pow2, (mp_limb_t)base);
|
||
|
pow2 = BITS_PER_MP_LIMB - pow2 - 1;
|
||
|
if (base != (1<<pow2)) pow2=0;
|
||
|
/* if pow2 <> 0, then base = 2^pow2 */
|
||
|
|
||
|
/* first determines the exponent */
|
||
|
e = EXP(op);
|
||
|
d = fabs(mpfr_get_d2(op, 0));
|
||
|
/* the absolute value of op is between 1/2*2^e and 2^e */
|
||
|
/* the output exponent f is such that base^(f-1) <= |op| < base^f
|
||
|
i.e. f = 1 + floor(log(|op|)/log(base))
|
||
|
= 1 + floor((log(|m|)+e*log(2))/log(base)) */
|
||
|
f = 1 + (int) floor((log(d)+(double)e*log(2.0))/log((double)base));
|
||
|
if (n==0) {
|
||
|
/* performs exact rounding, i.e. returns y such that for rnd_mode=RNDN
|
||
|
for example, we have:
|
||
|
y*base^(f-n) <= x*2^(e-p) < (x+1)*2^(e-p) <= (y+1)*base^(f-n)
|
||
|
which implies 2^(EXP(op)-PREC(op)) <= base^(f-n)
|
||
|
*/
|
||
|
n = f + (int) ceil(((double)PREC(op)-e)*log(2.0)/log((double)base));
|
||
|
}
|
||
|
/* now the first n digits of the mantissa are obtained from
|
||
|
rnd(op*base^(n-f)) */
|
||
|
prec = (long) ceil((double)n*log((double)base)/log(2.0));
|
||
|
err = 5;
|
||
|
q = prec+err;
|
||
|
/* one has to use at least q bits */
|
||
|
q = (((q-1)/BITS_PER_MP_LIMB)+1)*BITS_PER_MP_LIMB;
|
||
|
mpfr_init2(a,q); mpfr_init2(b,q);
|
||
|
|
||
|
do {
|
||
|
p = n-f; if ((div=(p<0))) p=-p;
|
||
|
rnd1 = rnd_mode;
|
||
|
if (div) {
|
||
|
/* if div we divide by base^p so we have to invert the rounding mode */
|
||
|
switch (rnd1) {
|
||
|
case GMP_RNDN: rnd1=GMP_RNDN; break;
|
||
|
case GMP_RNDZ: rnd1=GMP_RNDU; break;
|
||
|
case GMP_RNDU: rnd1=GMP_RNDZ; break;
|
||
|
case GMP_RNDD: rnd1=GMP_RNDZ; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pow2) {
|
||
|
if (div) mpfr_div_2exp(b, op, pow2*p, rnd_mode);
|
||
|
else mpfr_mul_2exp(b, op, pow2*p, rnd_mode);
|
||
|
}
|
||
|
else {
|
||
|
/* compute base^p with q bits and rounding towards zero */
|
||
|
mpfr_set_prec(b, q);
|
||
|
if (p==0) { mpfr_set(b, op, rnd_mode); mpfr_set_ui(a, 1, rnd_mode); }
|
||
|
else {
|
||
|
mpfr_set_prec(a, q);
|
||
|
mpfr_ui_pow_ui(a, base, p, rnd1);
|
||
|
if (div) {
|
||
|
mpfr_set_ui(b, 1, rnd_mode);
|
||
|
mpfr_div(a, b, a, rnd_mode);
|
||
|
}
|
||
|
/* now a is an approximation by default of 1/base^(f-n) */
|
||
|
mpfr_mul(b, op, a, rnd_mode);
|
||
|
}
|
||
|
}
|
||
|
if (neg) CHANGE_SIGN(b); /* put b positive */
|
||
|
|
||
|
if (q>2*prec+BITS_PER_MP_LIMB) {
|
||
|
/* happens when just in the middle between two digits */
|
||
|
n--; q-=BITS_PER_MP_LIMB;
|
||
|
if (n==0) {
|
||
|
fprintf(stderr, "cannot determine leading digit\n"); exit(1);
|
||
|
}
|
||
|
}
|
||
|
ok = pow2 || mpfr_can_round(b, q-err, rnd_mode, rnd_mode, prec);
|
||
|
|
||
|
if (ok) {
|
||
|
if (pow2) {
|
||
|
sh = e-PREC(op) + pow2*(n-f); /* error at most 2^e */
|
||
|
ok = mpfr_can_round(b, EXP(b)-sh-1, rnd_mode, rnd_mode, n*pow2);
|
||
|
}
|
||
|
else {
|
||
|
/* check that value is the same at distance 2^(e-PREC(op))/base^(f-n)
|
||
|
in opposite from rounding direction */
|
||
|
if (e>=PREC(op)) mpfr_mul_2exp(a, a, e-PREC(op), rnd_mode);
|
||
|
else mpfr_div_2exp(a, a, PREC(op)-e, rnd_mode);
|
||
|
if (rnd_mode==GMP_RNDN) {
|
||
|
mpfr_div_2exp(a, a, 2, rnd_mode);
|
||
|
mpfr_sub(b, b, a, rnd_mode); /* b - a/2 */
|
||
|
mpfr_mul_2exp(a, a, 2, rnd_mode);
|
||
|
mpfr_add(a, b, a, rnd_mode); /* b + a/2 */
|
||
|
}
|
||
|
else if ((rnd_mode==GMP_RNDU && neg==0) || (rnd_mode==GMP_RNDD && neg))
|
||
|
mpfr_sub(a, b, a, rnd_mode);
|
||
|
else mpfr_add(a, b, a, rnd_mode);
|
||
|
/* check that a and b are rounded similarly */
|
||
|
prec=EXP(b);
|
||
|
if (EXP(a) != prec) ok=0;
|
||
|
else {
|
||
|
mpfr_round(b, rnd_mode, prec);
|
||
|
mpfr_round(a, rnd_mode, prec);
|
||
|
if (mpfr_cmp(a, b)) ok=0;
|
||
|
}
|
||
|
}
|
||
|
if (ok==0) { /* n is too large */
|
||
|
n--;
|
||
|
if (n==0) {
|
||
|
fprintf(stderr, "cannot determine leading digit\n"); exit(1);
|
||
|
}
|
||
|
q -= BITS_PER_MP_LIMB;
|
||
|
}
|
||
|
}
|
||
|
} while (ok==0 && (q+=BITS_PER_MP_LIMB) );
|
||
|
if (neg)
|
||
|
switch (rnd_mode) {
|
||
|
case GMP_RNDU: rnd_mode=GMP_RNDZ; break;
|
||
|
case GMP_RNDD: rnd_mode=GMP_RNDU; break;
|
||
|
}
|
||
|
|
||
|
prec=EXP(b); /* may have changed due to rounding */
|
||
|
|
||
|
/* now the mantissa is the integer part of b */
|
||
|
mpz_init(bz); q=1+(prec-1)/BITS_PER_MP_LIMB;
|
||
|
_mpz_realloc(bz, q);
|
||
|
sh = prec%BITS_PER_MP_LIMB;
|
||
|
e = 1 + (PREC(b)-1)/BITS_PER_MP_LIMB-q;
|
||
|
if (sh) mpn_rshift(PTR(bz), MANT(b)+e, q, BITS_PER_MP_LIMB-sh);
|
||
|
else MPN_COPY(PTR(bz), MANT(b)+e, q);
|
||
|
bz->_mp_size=q;
|
||
|
|
||
|
/* computes the number of characters needed */
|
||
|
q = neg + n + 2; /* n+1 may not be enough for 100000... */
|
||
|
if (str==NULL) str0=str=(*_mp_allocate_func)(q);
|
||
|
if (neg) *str++='-';
|
||
|
mpz_get_str(str, base, bz); /* n digits of mantissa */
|
||
|
if (strlen(str)==n+1) f++; /* possible due to rounding */
|
||
|
*expptr = f;
|
||
|
mpfr_clear(a); mpfr_clear(b); mpz_clear(bz);
|
||
|
return str0;
|
||
|
}
|
||
|
|