macssh/gmp/mpfr/set_d.c

330 lines
7.2 KiB
C
Executable File

/* mpfr_set_d, mpfr_get_d -- convert a multiple precision floating-point number
from/to a machine double precision float
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. */
#if __GNUC__ /* gcc "patched" headers seem to omit isnan... */
extern int isnan(double);
#endif
#include <math.h> /* for isnan and NaN */
#include "gmp.h"
#include "gmp-impl.h"
#include "longlong.h"
#include "mpfr.h"
#define NaN sqrt(-1) /* ensures a machine-independent NaN */
/* Included from gmp-2.0.2, patched to support denorms */
#ifdef XDEBUG
#undef _GMP_IEEE_FLOATS
#endif
#ifndef _GMP_IEEE_FLOATS
#define _GMP_IEEE_FLOATS 0
#endif
int
#if __STDC__
__mpfr_extract_double (mp_ptr rp, double d, int e)
#else
__mpfr_extract_double (rp, d, e)
mp_ptr rp;
double d;
int e;
#endif
/* e=0 iff BITS_PER_MP_LIMB=32 and rp has only one limb */
{
long exp;
mp_limb_t manl;
#if BITS_PER_MP_LIMB == 32
mp_limb_t manh;
#endif
/* BUGS
1. Should handle Inf and NaN in IEEE specific code.
2. Handle Inf and NaN also in default code, to avoid hangs.
3. Generalize to handle all BITS_PER_MP_LIMB >= 32.
4. This lits is incomplete and misspelled.
*/
if (d == 0.0)
{
rp[0] = 0;
#if BITS_PER_MP_LIMB == 32
if (e) rp[1] = 0;
#endif
return 0;
}
#if _GMP_IEEE_FLOATS
{
union ieee_double_extract x;
x.d = d;
exp = x.s.exp;
if (exp)
{
#if BITS_PER_MP_LIMB == 64
manl = (((mp_limb_t) 1 << 63)
| ((mp_limb_t) x.s.manh << 43) | ((mp_limb_t) x.s.manl << 11));
#else
manh = ((mp_limb_t) 1 << 31) | (x.s.manh << 11) | (x.s.manl >> 21);
manl = x.s.manl << 11;
#endif
}
else
{
#if BITS_PER_MP_LIMB == 64
manl = ((mp_limb_t) x.s.manh << 43) | ((mp_limb_t) x.s.manl << 11);
#else
manh = (x.s.manh << 11) | (x.s.manl >> 21);
manl = x.s.manl << 11;
#endif
}
}
#else
{
/* Unknown (or known to be non-IEEE) double format. */
exp = 0;
if (d >= 1.0)
{
if (d * 0.5 == d)
abort ();
while (d >= 32768.0)
{
d *= (1.0 / 65536.0);
exp += 16;
}
while (d >= 1.0)
{
d *= 0.5;
exp += 1;
}
}
else if (d < 0.5)
{
while (d < (1.0 / 65536.0))
{
d *= 65536.0;
exp -= 16;
}
while (d < 0.5)
{
d *= 2.0;
exp -= 1;
}
}
d *= MP_BASE_AS_DOUBLE;
#if BITS_PER_MP_LIMB == 64
manl = d;
#else
manh = d;
manl = (d - manh) * MP_BASE_AS_DOUBLE;
#endif
exp += 1022;
}
#endif
if (exp) exp = (unsigned) exp - 1022; else exp = -1021;
#if BITS_PER_MP_LIMB == 64
rp[0] = manl;
#else
if (e) {
rp[1] = manh;
rp[0] = manl;
}
else {
rp[0] = manh;
}
#endif
return exp;
}
/* End of part included from gmp-2.0.2 */
/* Part included from gmp temporary releases */
double
#if __STDC__
__mpfr_scale2 (double d, int exp)
#else
__mpfr_scale2 (d, exp)
double d;
int exp;
#endif
{
#if _GMP_IEEE_FLOATS
{
union ieee_double_extract x;
x.d = d;
exp += x.s.exp;
x.s.exp = exp;
if (exp >= 2047)
{
/* Return +-infinity */
x.s.exp = 2047;
x.s.manl = x.s.manh = 0;
}
else if (exp < 1)
{
x.s.exp = 1; /* smallest exponent (biased) */
/* Divide result by 2 until we have scaled it to the right IEEE
denormalized number, but stop if it becomes zero. */
while (exp < 1 && x.d != 0)
{
x.d *= 0.5;
exp++;
}
}
return x.d;
}
#else
{
double factor, r;
factor = 2.0;
if (exp < 0)
{
factor = 0.5;
exp = -exp;
}
r = d;
if (exp != 0)
{
if ((exp & 1) != 0)
r *= factor;
exp >>= 1;
while (exp != 0)
{
factor *= factor;
if ((exp & 1) != 0)
r *= factor;
exp >>= 1;
}
}
return r;
}
#endif
}
/* End of part included from gmp */
void
#if __STDC__
mpfr_set_d(mpfr_t r, double d, unsigned char rnd_mode)
#else
mpfr_set_d(r, d, rnd_mode)
mpfr_t r;
double d;
unsigned char rnd_mode;
#endif
{
int signd, sizer; unsigned int cnt;
if (d == 0) { SET_ZERO(r); return; }
else if (isnan(d)) { SET_NAN(r); return; }
signd = (d < 0) ? -1 : 1;
d = ABS (d);
sizer = (PREC(r)-1)/BITS_PER_MP_LIMB + 1;
/* warning: __mpfr_extract_double requires at least two limbs */
if (sizer < MPFR_LIMBS_PER_DOUBLE)
EXP(r) = __mpfr_extract_double (MANT(r), d, 0);
else
EXP(r) = __mpfr_extract_double (MANT(r) + sizer - MPFR_LIMBS_PER_DOUBLE, d, 1);
if (sizer > MPFR_LIMBS_PER_DOUBLE)
MPN_ZERO(MANT(r), sizer - MPFR_LIMBS_PER_DOUBLE);
count_leading_zeros(cnt, MANT(r)[sizer-1]);
if (cnt) mpn_lshift(MANT(r), MANT(r), sizer, cnt);
EXP(r) -= cnt;
if (SIZE(r)*signd<0) CHANGE_SIGN(r);
mpfr_round(r, rnd_mode, PREC(r));
return;
}
double
#if __STDC__
mpfr_get_d2(mpfr_srcptr src, long e)
#else
mpfr_get_d2(src, e)
mpfr_srcptr(src);
long e;
#endif
{
double res;
mp_size_t size, i, n_limbs_to_use;
mp_ptr qp;
int negative;
if (FLAG_NAN(src)) {
#ifdef DEBUG
printf("recognized NaN\n");
#endif
return NaN; }
if (NOTZERO(src)==0) return 0.0;
size = 1+(PREC(src)-1)/BITS_PER_MP_LIMB;
qp = MANT(src);
negative = (SIGN(src)==-1);
/* Warning: don't compute the abs(res) and set the sign afterwards,
otherwise the current machine rounding mode will not be taken
correctly into account. */
/* res = (negative) ? -(double)qp[size - 1] : qp[size - 1]; */
res = 0.0;
/* Warning: an arbitrary number of limbs may be required for an exact
rounding. The following code is correct but not optimal since one
may be able to decide without considering all limbs. */
/* n_limbs_to_use = MIN (MPFR_LIMBS_PER_DOUBLE, size); */
n_limbs_to_use = size;
/* Accumulate the limbs from less significant to most significant
otherwise due to rounding we may accumulate several ulps,
especially in rounding towards -/+infinity. */
for (i = n_limbs_to_use; i>=1; i--)
res = res / MP_BASE_AS_DOUBLE +
((negative) ? -(double)qp[size - i] : qp[size - i]);
res = __mpfr_scale2 (res, e - BITS_PER_MP_LIMB);
return res;
}
double
#if __STDC__
mpfr_get_d(mpfr_srcptr src)
#else
mpfr_get_d(src)
mpfr_srcptr src;
#endif
{
return mpfr_get_d2(src, EXP(src));
}