1e2bffd05c
Continuing the move of libm aliases to common macros that can create _FloatN / _FloatNx aliases in future, this patch converts some dbl-64 functions to using libm_alias_double, thereby eliminating the need for some ldbl-opt wrappers. This patch deliberately limits what functions are converted so that it can be verified by comparison of stipped binaries. Specifically, atan and tan are excluded because they first need converting to being weak aliases; fma is omitted as it has additional complications with versions in other directories (removing the ldbl-opt version can e.g. cause the ldbl-128 version to be used instead of dbl-64); and functions that have both dbl-64/wordsize-64 and ldbl-opt versions are excluded because ldbl-opt currently always wraps dbl-64 function versions, so changing those will result in platforms using both ldbl-opt and dbl-64/wordsize-64 (i.e. alpha) starting to use the dbl-64/wordsize-64 versions of those functions (which is good, as an optimization, but still best separated from the present patch to get better validation). Tested for x86_64, and tested with build-many-glibcs.py that installed stripped shared libraries are unchanged by the patch. * sysdeps/ieee754/dbl-64/s_asinh.c: Include <libm-alias-double.h>. (asinh): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_cbrt.c: Include <libm-alias-double.h>. (cbrt): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_copysign.c: Include <libm-alias-double.h>. (copysign): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_erf.c: Include <libm-alias-double.h>. (erf): Define using libm_alias_double. (erfc): Likewise. * sysdeps/ieee754/dbl-64/s_expm1.c: Include <libm-alias-double.h>. (expm1): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_fabs.c: Include <libm-alias-double.h>. (fabs): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_fromfp.c (fromfp): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_fromfp_main.c: Include <libm-alias-double.h>. * sysdeps/ieee754/dbl-64/s_fromfpx.c (fromfpx): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_getpayload.c: Include <libm-alias-double.h>. (getpayload): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_llrint.c: Include <libm-alias-double.h>. (llrint): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_lrint.c: Include <libm-alias-double.h>. (lrint): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_nextup.c: Include <libm-alias-double.h>. (nextup): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_roundeven.c: Include <libm-alias-double.h>. (roundeven): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_setpayload.c (setpayload): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_setpayload_main.c: Include <libm-alias-double.h>. * sysdeps/ieee754/dbl-64/s_setpayloadsig.c (setpayloadsig): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_sin.c: Include <libm-alias-double.h>. (cos): Define using libm_alias_double. (sin): Likewise. * sysdeps/ieee754/dbl-64/s_sincos.c: Include <libm-alias-double.h>. (sincos): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_tanh.c: Include <libm-alias-double.h>. (tanh): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_totalorder.c: Include <libm-alias-double.h>. (totalorder): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_totalordermag.c: Include <libm-alias-double.h>. (totalordermag): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_ufromfp.c (ufromfp): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/s_ufromfpx.c (ufromfpx): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/wordsize-64/s_getpayload.c: Include <libm-alias-double.h>. (getpayload): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/wordsize-64/s_roundeven.c: Include <libm-alias-double.h>. (roundeven): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/wordsize-64/s_setpayload_main.c: Include <libm-alias-double.h>. * sysdeps/ieee754/dbl-64/wordsize-64/s_totalorder.c: Include <libm-alias-double.h>. (totalorder): Define using libm_alias_double. * sysdeps/ieee754/dbl-64/wordsize-64/s_totalordermag.c: Include <libm-alias-double.h>. (totalordermag): Define using libm_alias_double. * sysdeps/ieee754/ldbl-opt/s_copysign.c (copysignl): Only define libc compat symbol here. * sysdeps/ieee754/ldbl-opt/s_asinh.c: Remove file. * sysdeps/ieee754/ldbl-opt/s_cbrt.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_erf.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_expm1.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_fabs.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_llrint.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_lrint.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_sin.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_sincos.c: Likewise. * sysdeps/ieee754/ldbl-opt/s_tanh.c: Likewise.
260 lines
8.0 KiB
C
260 lines
8.0 KiB
C
/* @(#)s_expm1.c 5.1 93/09/24 */
|
|
/*
|
|
* ====================================================
|
|
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
|
*
|
|
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
|
* Permission to use, copy, modify, and distribute this
|
|
* software is freely granted, provided that this notice
|
|
* is preserved.
|
|
* ====================================================
|
|
*/
|
|
/* Modified by Naohiko Shimizu/Tokai University, Japan 1997/08/25,
|
|
for performance improvement on pipelined processors.
|
|
*/
|
|
|
|
/* expm1(x)
|
|
* Returns exp(x)-1, the exponential of x minus 1.
|
|
*
|
|
* Method
|
|
* 1. Argument reduction:
|
|
* Given x, find r and integer k such that
|
|
*
|
|
* x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658
|
|
*
|
|
* Here a correction term c will be computed to compensate
|
|
* the error in r when rounded to a floating-point number.
|
|
*
|
|
* 2. Approximating expm1(r) by a special rational function on
|
|
* the interval [0,0.34658]:
|
|
* Since
|
|
* r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ...
|
|
* we define R1(r*r) by
|
|
* r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r)
|
|
* That is,
|
|
* R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r)
|
|
* = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r))
|
|
* = 1 - r^2/60 + r^4/2520 - r^6/100800 + ...
|
|
* We use a special Reme algorithm on [0,0.347] to generate
|
|
* a polynomial of degree 5 in r*r to approximate R1. The
|
|
* maximum error of this polynomial approximation is bounded
|
|
* by 2**-61. In other words,
|
|
* R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5
|
|
* where Q1 = -1.6666666666666567384E-2,
|
|
* Q2 = 3.9682539681370365873E-4,
|
|
* Q3 = -9.9206344733435987357E-6,
|
|
* Q4 = 2.5051361420808517002E-7,
|
|
* Q5 = -6.2843505682382617102E-9;
|
|
* (where z=r*r, and the values of Q1 to Q5 are listed below)
|
|
* with error bounded by
|
|
* | 5 | -61
|
|
* | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2
|
|
* | |
|
|
*
|
|
* expm1(r) = exp(r)-1 is then computed by the following
|
|
* specific way which minimize the accumulation rounding error:
|
|
* 2 3
|
|
* r r [ 3 - (R1 + R1*r/2) ]
|
|
* expm1(r) = r + --- + --- * [--------------------]
|
|
* 2 2 [ 6 - r*(3 - R1*r/2) ]
|
|
*
|
|
* To compensate the error in the argument reduction, we use
|
|
* expm1(r+c) = expm1(r) + c + expm1(r)*c
|
|
* ~ expm1(r) + c + r*c
|
|
* Thus c+r*c will be added in as the correction terms for
|
|
* expm1(r+c). Now rearrange the term to avoid optimization
|
|
* screw up:
|
|
* ( 2 2 )
|
|
* ({ ( r [ R1 - (3 - R1*r/2) ] ) } r )
|
|
* expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- )
|
|
* ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 )
|
|
* ( )
|
|
*
|
|
* = r - E
|
|
* 3. Scale back to obtain expm1(x):
|
|
* From step 1, we have
|
|
* expm1(x) = either 2^k*[expm1(r)+1] - 1
|
|
* = or 2^k*[expm1(r) + (1-2^-k)]
|
|
* 4. Implementation notes:
|
|
* (A). To save one multiplication, we scale the coefficient Qi
|
|
* to Qi*2^i, and replace z by (x^2)/2.
|
|
* (B). To achieve maximum accuracy, we compute expm1(x) by
|
|
* (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf)
|
|
* (ii) if k=0, return r-E
|
|
* (iii) if k=-1, return 0.5*(r-E)-0.5
|
|
* (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E)
|
|
* else return 1.0+2.0*(r-E);
|
|
* (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1)
|
|
* (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else
|
|
* (vii) return 2^k(1-((E+2^-k)-r))
|
|
*
|
|
* Special cases:
|
|
* expm1(INF) is INF, expm1(NaN) is NaN;
|
|
* expm1(-INF) is -1, and
|
|
* for finite argument, only expm1(0)=0 is exact.
|
|
*
|
|
* Accuracy:
|
|
* according to an error analysis, the error is always less than
|
|
* 1 ulp (unit in the last place).
|
|
*
|
|
* Misc. info.
|
|
* For IEEE double
|
|
* if x > 7.09782712893383973096e+02 then expm1(x) overflow
|
|
*
|
|
* Constants:
|
|
* The hexadecimal values are the intended ones for the following
|
|
* constants. The decimal values may be used, provided that the
|
|
* compiler will convert from decimal to binary accurately enough
|
|
* to produce the hexadecimal values shown.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <float.h>
|
|
#include <math.h>
|
|
#include <math_private.h>
|
|
#include <libm-alias-double.h>
|
|
#define one Q[0]
|
|
static const double
|
|
huge = 1.0e+300,
|
|
tiny = 1.0e-300,
|
|
o_threshold = 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */
|
|
ln2_hi = 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */
|
|
ln2_lo = 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */
|
|
invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */
|
|
/* scaled coefficients related to expm1 */
|
|
Q[] = { 1.0, -3.33333333333331316428e-02, /* BFA11111 111110F4 */
|
|
1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */
|
|
-7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */
|
|
4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */
|
|
-2.01099218183624371326e-07 }; /* BE8AFDB7 6E09C32D */
|
|
|
|
double
|
|
__expm1 (double x)
|
|
{
|
|
double y, hi, lo, c, t, e, hxs, hfx, r1, h2, h4, R1, R2, R3;
|
|
int32_t k, xsb;
|
|
uint32_t hx;
|
|
|
|
GET_HIGH_WORD (hx, x);
|
|
xsb = hx & 0x80000000; /* sign bit of x */
|
|
if (xsb == 0)
|
|
y = x;
|
|
else
|
|
y = -x; /* y = |x| */
|
|
hx &= 0x7fffffff; /* high word of |x| */
|
|
|
|
/* filter out huge and non-finite argument */
|
|
if (hx >= 0x4043687A) /* if |x|>=56*ln2 */
|
|
{
|
|
if (hx >= 0x40862E42) /* if |x|>=709.78... */
|
|
{
|
|
if (hx >= 0x7ff00000)
|
|
{
|
|
uint32_t low;
|
|
GET_LOW_WORD (low, x);
|
|
if (((hx & 0xfffff) | low) != 0)
|
|
return x + x; /* NaN */
|
|
else
|
|
return (xsb == 0) ? x : -1.0; /* exp(+-inf)={inf,-1} */
|
|
}
|
|
if (x > o_threshold)
|
|
{
|
|
__set_errno (ERANGE);
|
|
return huge * huge; /* overflow */
|
|
}
|
|
}
|
|
if (xsb != 0) /* x < -56*ln2, return -1.0 with inexact */
|
|
{
|
|
math_force_eval (x + tiny); /* raise inexact */
|
|
return tiny - one; /* return -1 */
|
|
}
|
|
}
|
|
|
|
/* argument reduction */
|
|
if (hx > 0x3fd62e42) /* if |x| > 0.5 ln2 */
|
|
{
|
|
if (hx < 0x3FF0A2B2) /* and |x| < 1.5 ln2 */
|
|
{
|
|
if (xsb == 0)
|
|
{
|
|
hi = x - ln2_hi; lo = ln2_lo; k = 1;
|
|
}
|
|
else
|
|
{
|
|
hi = x + ln2_hi; lo = -ln2_lo; k = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
k = invln2 * x + ((xsb == 0) ? 0.5 : -0.5);
|
|
t = k;
|
|
hi = x - t * ln2_hi; /* t*ln2_hi is exact here */
|
|
lo = t * ln2_lo;
|
|
}
|
|
x = hi - lo;
|
|
c = (hi - x) - lo;
|
|
}
|
|
else if (hx < 0x3c900000) /* when |x|<2**-54, return x */
|
|
{
|
|
math_check_force_underflow (x);
|
|
t = huge + x; /* return x with inexact flags when x!=0 */
|
|
return x - (t - (huge + x));
|
|
}
|
|
else
|
|
k = 0;
|
|
|
|
/* x is now in primary range */
|
|
hfx = 0.5 * x;
|
|
hxs = x * hfx;
|
|
R1 = one + hxs * Q[1]; h2 = hxs * hxs;
|
|
R2 = Q[2] + hxs * Q[3]; h4 = h2 * h2;
|
|
R3 = Q[4] + hxs * Q[5];
|
|
r1 = R1 + h2 * R2 + h4 * R3;
|
|
t = 3.0 - r1 * hfx;
|
|
e = hxs * ((r1 - t) / (6.0 - x * t));
|
|
if (k == 0)
|
|
return x - (x * e - hxs); /* c is 0 */
|
|
else
|
|
{
|
|
e = (x * (e - c) - c);
|
|
e -= hxs;
|
|
if (k == -1)
|
|
return 0.5 * (x - e) - 0.5;
|
|
if (k == 1)
|
|
{
|
|
if (x < -0.25)
|
|
return -2.0 * (e - (x + 0.5));
|
|
else
|
|
return one + 2.0 * (x - e);
|
|
}
|
|
if (k <= -2 || k > 56) /* suffice to return exp(x)-1 */
|
|
{
|
|
uint32_t high;
|
|
y = one - (e - x);
|
|
GET_HIGH_WORD (high, y);
|
|
SET_HIGH_WORD (y, high + (k << 20)); /* add k to y's exponent */
|
|
return y - one;
|
|
}
|
|
t = one;
|
|
if (k < 20)
|
|
{
|
|
uint32_t high;
|
|
SET_HIGH_WORD (t, 0x3ff00000 - (0x200000 >> k)); /* t=1-2^-k */
|
|
y = t - (e - x);
|
|
GET_HIGH_WORD (high, y);
|
|
SET_HIGH_WORD (y, high + (k << 20)); /* add k to y's exponent */
|
|
}
|
|
else
|
|
{
|
|
uint32_t high;
|
|
SET_HIGH_WORD (t, ((0x3ff - k) << 20)); /* 2^-k */
|
|
y = x - (e + t);
|
|
y += one;
|
|
GET_HIGH_WORD (high, y);
|
|
SET_HIGH_WORD (y, high + (k << 20)); /* add k to y's exponent */
|
|
}
|
|
}
|
|
return y;
|
|
}
|
|
libm_alias_double (__expm1, expm1)
|