mirror of
https://github.com/vale981/arb
synced 2025-03-05 09:21:38 -05:00
206 lines
4.3 KiB
C
206 lines
4.3 KiB
C
/*
|
|
Copyright (C) 2014 Fredrik Johansson
|
|
|
|
This file is part of Arb.
|
|
|
|
Arb is free software: you can redistribute it and/or modify it under
|
|
the terms of the GNU Lesser General Public License (LGPL) as published
|
|
by the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version. See <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "flint/double_extras.h"
|
|
#include "mag.h"
|
|
|
|
static const double inverse_factorials[] = {
|
|
1.0,
|
|
1.0,
|
|
0.5,
|
|
0.16666666666666666667,
|
|
0.041666666666666666667,
|
|
0.0083333333333333333333,
|
|
0.0013888888888888888889,
|
|
0.0001984126984126984127,
|
|
0.000024801587301587301587,
|
|
2.7557319223985890653e-6,
|
|
2.7557319223985890653e-7,
|
|
2.5052108385441718775e-8,
|
|
2.0876756987868098979e-9,
|
|
1.6059043836821614599e-10,
|
|
1.1470745597729724714e-11,
|
|
7.6471637318198164759e-13
|
|
};
|
|
|
|
/* warning: requires |x| < 2^24 or thereabout */
|
|
void
|
|
_mag_exp_d(mag_t res, double x, int roundup)
|
|
{
|
|
double u, nlog2, eps, eps2;
|
|
slong n;
|
|
|
|
if (roundup)
|
|
{
|
|
eps = 1e-13;
|
|
eps2 = 6e-13;
|
|
}
|
|
else
|
|
{
|
|
eps = -1e-13;
|
|
eps2 = -6e-13;
|
|
}
|
|
|
|
n = floor(x * 1.4426950408889634074 + 0.5);
|
|
|
|
/* does not need to be exact */
|
|
/* perturbed in opposite direction before subtraction */
|
|
if (n >= 0)
|
|
nlog2 = n * 0.69314718055994530942 * (1.0 - eps);
|
|
else
|
|
nlog2 = n * 0.69314718055994530942 * (1.0 + eps);
|
|
|
|
/* perturbed (assuming |u| < 1 for absolute error to be valid) */
|
|
u = x - nlog2 + eps;
|
|
|
|
if (u >= -0.375 && u <= 0.375)
|
|
u = d_polyval(inverse_factorials, 11, u) + eps2;
|
|
else
|
|
flint_abort();
|
|
|
|
if (roundup)
|
|
mag_set_d(res, u);
|
|
else
|
|
mag_set_d_lower(res, u);
|
|
|
|
MAG_EXP(res) += n; /* assumes x not too large */
|
|
}
|
|
|
|
void
|
|
mag_exp_huge(mag_t res, const mag_t x)
|
|
{
|
|
if (mag_cmp_2exp_si(x, 128) <= 0)
|
|
{
|
|
fmpz_t t;
|
|
fmpz_init(t);
|
|
mag_get_fmpz(t, x);
|
|
MAG_MAN(res) = 729683223; /* upper bound for e */
|
|
fmpz_set_ui(MAG_EXPREF(res), 2);
|
|
mag_pow_fmpz(res, res, t);
|
|
fmpz_clear(t);
|
|
}
|
|
else
|
|
{
|
|
mag_inf(res);
|
|
}
|
|
}
|
|
|
|
void
|
|
mag_exp_huge_lower(mag_t res, const mag_t x)
|
|
{
|
|
fmpz_t t;
|
|
fmpz_init(t);
|
|
|
|
if (mag_cmp_2exp_si(x, 128) <= 0)
|
|
{
|
|
mag_get_fmpz_lower(t, x);
|
|
}
|
|
else
|
|
{
|
|
fmpz_one(t);
|
|
fmpz_mul_2exp(t, t, 128);
|
|
}
|
|
|
|
MAG_MAN(res) = 729683222; /* lower bound for e */
|
|
fmpz_set_ui(MAG_EXPREF(res), 2);
|
|
mag_pow_fmpz_lower(res, res, t);
|
|
fmpz_clear(t);
|
|
}
|
|
|
|
void
|
|
mag_exp(mag_t y, const mag_t x)
|
|
{
|
|
if (mag_is_special(x))
|
|
{
|
|
if (mag_is_zero(x))
|
|
mag_one(y);
|
|
else
|
|
mag_inf(y);
|
|
}
|
|
else if (COEFF_IS_MPZ(MAG_EXP(x)))
|
|
{
|
|
if (fmpz_sgn(MAG_EXPREF(x)) > 0)
|
|
{
|
|
mag_inf(y);
|
|
}
|
|
else
|
|
{
|
|
MAG_MAN(y) = MAG_ONE_HALF + 1;
|
|
fmpz_one(MAG_EXPREF(y));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slong e = MAG_EXP(x);
|
|
|
|
if (e <= -MAG_BITS) /* assumes MAG_BITS == 30 */
|
|
{
|
|
MAG_MAN(y) = MAG_ONE_HALF + 1;
|
|
fmpz_one(MAG_EXPREF(y));
|
|
}
|
|
else if (e <= -(MAG_BITS / 2)) /* assumes MAG_BITS == 30 */
|
|
{
|
|
MAG_MAN(y) = MAG_ONE_HALF + (MAG_MAN(x) >> (1-e)) + 2;
|
|
fmpz_one(MAG_EXPREF(y));
|
|
}
|
|
else if (e < 24)
|
|
{
|
|
_mag_exp_d(y, ldexp(MAG_MAN(x), e - MAG_BITS), 1);
|
|
}
|
|
else
|
|
{
|
|
mag_exp_huge(y, x);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
mag_exp_lower(mag_t y, const mag_t x)
|
|
{
|
|
if (mag_is_special(x))
|
|
{
|
|
if (mag_is_zero(x))
|
|
mag_one(y);
|
|
else
|
|
mag_inf(y);
|
|
}
|
|
else if (COEFF_IS_MPZ(MAG_EXP(x)))
|
|
{
|
|
if (fmpz_sgn(MAG_EXPREF(x)) > 0)
|
|
mag_exp_huge_lower(y, x);
|
|
else
|
|
mag_one(y);
|
|
}
|
|
else
|
|
{
|
|
slong e = MAG_EXP(x);
|
|
|
|
if (e <= -MAG_BITS)
|
|
{
|
|
mag_one(y);
|
|
}
|
|
else if (e <= -(MAG_BITS / 2))
|
|
{
|
|
MAG_MAN(y) = MAG_ONE_HALF + (MAG_MAN(x) >> (1 - e));
|
|
fmpz_one(MAG_EXPREF(y));
|
|
}
|
|
else if (e < 24)
|
|
{
|
|
_mag_exp_d(y, ldexp(MAG_MAN(x), e - MAG_BITS), 0);
|
|
}
|
|
else
|
|
{
|
|
mag_exp_huge_lower(y, x);
|
|
}
|
|
}
|
|
}
|
|
|
|
|