Hi, this adds the expm1 method to java.lang.StrictMath.
Please comment/commit. Thanks, Carsten 2006-07-22 Carsten Neumann <[EMAIL PROTECTED]> * StrictMath.java (expm1): New method. (EXPM1_Q1): New field. (EXPM1_Q2): Likewise. (EXPM1_Q3): Likewise. (EXPM1_Q4): Likewise. (EXPM1_Q6): Likewise.
Index: java/lang/StrictMath.java =================================================================== RCS file: /sources/classpath/classpath/java/lang/StrictMath.java,v retrieving revision 1.10 diff -u -r1.10 StrictMath.java --- java/lang/StrictMath.java 16 Jul 2006 20:23:49 -0000 1.10 +++ java/lang/StrictMath.java 22 Jul 2006 14:50:39 -0000 @@ -819,6 +819,254 @@ } /** + * Returns <em>e</em><sup>x</sup> - 1. + * Special cases: + * <ul> + * <li>If the argument is NaN, the result is NaN.</li> + * <li>If the argument is positive infinity, the result is positive + * infinity</li> + * <li>If the argument is negative infinity, the result is -1.</li> + * <li>If the argument is zero, the result is zero.</li> + * </ul> + * + * @param x the argument to <em>e</em><sup>x</sup> - 1. + * @return <em>e</em> raised to the power <code>x</code> minus one. + * @see #exp(double) + */ + public static double expm1(double x) + { + // Method + // 1. Argument reduction: + // Given x, find r and integer k such that + // + // x = k * ln(2) + r, |r| <= 0.5 * ln(2) + // + // 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.5 * ln(2)]: + // 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 Remes 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 Q1 to Q5 are called EXPM1_Qx in the source) + // 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)) + + boolean negative = (x < 0); + double y, hi, lo, c, t, e, hxs, hfx, r1; + int k; + + long bits; + int h_bits; + int l_bits; + + c = 0.0; + y = abs(x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + // handle special cases and large arguments + if (h_bits >= 0x4043687a) // if |x| >= 56 * ln(2) + { + if (h_bits >= 0x40862e42) // if |x| >= EXP_LIMIT_H + { + if (h_bits >= 0x7ff00000) + { + if (((h_bits & 0x000fffff) | (l_bits & 0xffffffff)) != 0) + return Double.NaN; // exp(NaN) = NaN + else + return negative ? -1.0 : x; // exp({+-inf}) = {+inf, -1} + } + + if (x > EXP_LIMIT_H) + return Double.POSITIVE_INFINITY; // overflow + } + + if (negative) // x <= -56 * ln(2) + return -1.0; + } + + // argument reduction + if (h_bits > 0x3fd62e42) // |x| > 0.5 * ln(2) + { + if (h_bits < 0x3ff0a2b2) // |x| < 1.5 * ln(2) + { + if (negative) + { + hi = x + LN2_H; + lo = -LN2_L; + k = -1; + } + else + { + hi = x - LN2_H; + lo = LN2_L; + k = 1; + } + } + else + { + k = (int) (INV_LN2 * x + (negative ? - 0.5 : 0.5)); + t = k; + hi = x - t * LN2_H; + lo = t * LN2_L; + } + + x = hi - lo; + c = (hi - x) - lo; + + } + else if (h_bits < 0x3c900000) // |x| < 2^-54 return x + return x; + else + k = 0; + + // x is now in primary range + hfx = 0.5 * x; + hxs = x * hfx; + r1 = 1.0 + hxs * (EXPM1_Q1 + + hxs * (EXPM1_Q2 + + hxs * (EXPM1_Q3 + + hxs * (EXPM1_Q4 + + hxs * EXPM1_Q5)))); + t = 3.0 - r1 * hfx; + e = hxs * ((r1 - t) / (6.0 - x * t)); + + if (k == 0) + { + return x - (x * e - hxs); // c == 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 1.0 + 2.0 * (x - e); + } + + if (k <= -2 || k > 56) // sufficient to return exp(x) - 1 + { + y = 1.0 - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + + return y - 1.0; + } + + t = 1.0; + if (k < 20) + { + bits = Double.doubleToLongBits(t); + h_bits = 0x3ff00000 - (0x00200000 >> k); + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 1 - 2^(-k) + y = t - (e - x); + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + else + { + bits = Double.doubleToLongBits(t); + h_bits = (0x000003ff - k) << 20; + l_bits = getLowDWord(bits); + + t = buildDouble(l_bits, h_bits); // t = 2^(-k) + + y = x - (e + t); + y += 1.0; + + bits = Double.doubleToLongBits(y); + h_bits = getHighDWord(bits); + l_bits = getLowDWord(bits); + + h_bits += (k << 20); // add k to y's exponent + + y = buildDouble(l_bits, h_bits); + } + } + + return y; + } + + /** * Take ln(a) (the natural log). The opposite of <code>exp()</code>. If the * argument is NaN or negative, the result is NaN; if the argument is * positive infinity, the result is positive infinity; and if the argument @@ -1571,6 +1819,16 @@ CBRT_G = 3.57142857142857150787e-01; // Long bits 0x3fd6db6db6db6db7L /** + * Constants for computing [EMAIL PROTECTED] #expm1(double)} + */ + private static final double + EXPM1_Q1 = -3.33333333333331316428e-02, // Long bits 0xbfa11111111110f4L + EXPM1_Q2 = 1.58730158725481460165e-03, // Long bits 0x3f5a01a019fe5585L + EXPM1_Q3 = -7.93650757867487942473e-05, // Long bits 0xbf14ce199eaadbb7L + EXPM1_Q4 = 4.00821782732936239552e-06, // Long bits 0x3ed0cfca86e65239L + EXPM1_Q5 = -2.01099218183624371326e-07; // Long bits 0xbe8afdb76e09c32dL + + /** * Helper function for reducing an angle to a multiple of pi/2 within * [-pi/4, pi/4]. *