mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-30 00:31:08 +00:00
554edb23ff
Similar to various other bugs in this area, some expm1 implementations do not raise the underflow exception for subnormal arguments, when the result is tiny and inexact. This patch forces the exception in a similar way to previous fixes. (The issue does not apply to the ldbl-* implementations or to those for x86 / x86_64 long double. The change to sysdeps/ieee754/dbl-64/wordsize-64/e_cosh.c is one I missed when previously fixing bug 16354; the bug in that implementation was previously latent, but the expm1 fixes stopped it being latent and so required it to be fixed to avoid spurious underflows from cosh.) Tested for x86_64 and x86. [BZ #16353] * sysdeps/i386/fpu/s_expm1.S (dbl_min): New object. (__expm1): Force underflow exception for arguments with small absolute value. * sysdeps/i386/fpu/s_expm1f.S (flt_min): New object. (__expm1f): Force underflow exception for arguments with small absolute value. * sysdeps/ieee754/dbl-64/s_expm1.c: Include <float.h>. (__expm1): Force underflow exception for arguments with small absolute value. * sysdeps/ieee754/flt-32/s_expm1f.c: Include <float.h>. (__expm1f): Force underflow exception for arguments with small absolute value. * sysdeps/ieee754/dbl-64/wordsize-64/e_cosh.c (__ieee754_cosh): Check for small arguments before calling __expm1. * math/auto-libm-test-in: Do not mark underflow exceptions as possibly missing for bug 16353. * math/auto-libm-test-out: Regenerated.
267 lines
8.2 KiB
C
267 lines
8.2 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>
|
|
#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;
|
|
u_int32_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)
|
|
{
|
|
u_int32_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 */
|
|
{
|
|
if (fabs (x) < DBL_MIN)
|
|
{
|
|
double force_underflow = x * x;
|
|
math_force_eval (force_underflow);
|
|
}
|
|
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 */
|
|
{
|
|
u_int32_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)
|
|
{
|
|
u_int32_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
|
|
{
|
|
u_int32_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;
|
|
}
|
|
weak_alias (__expm1, expm1)
|
|
#ifdef NO_LONG_DOUBLE
|
|
strong_alias (__expm1, __expm1l)
|
|
weak_alias (__expm1, expm1l)
|
|
#endif
|