diff options
author | Martynas Venckus <martynas@cvs.openbsd.org> | 2008-06-11 20:53:28 +0000 |
---|---|---|
committer | Martynas Venckus <martynas@cvs.openbsd.org> | 2008-06-11 20:53:28 +0000 |
commit | 52b952ebc3ff31143605788b38d890798ea23055 (patch) | |
tree | 110c2ad3877e4b59f6d3f44b7b0aea18a3c1efdb /lib/libm/noieee_src | |
parent | 61b9a28afee59294427e885ebdc5a1f9313d0388 (diff) |
- on non-ieee, rename gamma to tgamma, the 'true' gamma
- make gamma an alias of lgamma
- on ieee, add tgamma, based on gamma from non-ieee
- fixes for tgamma/lgamma/exp/log, esp. special cases (some from
freebsd); properly raise invalid fp operations on vax
- also some general cleanup, ansification, man page (which was ok
jmc@)
- bump minor
this makes some ports using tgamma possible; also consistifies
behavior across openbsd/ieee and openbsd/non-ieee, and other operating
systems
much thanks sthen@, johan@, steven@, Simon Kuhnle, Wiktor Izdebski
for testing
ok millert@
Diffstat (limited to 'lib/libm/noieee_src')
-rw-r--r-- | lib/libm/noieee_src/n_exp.c | 32 | ||||
-rw-r--r-- | lib/libm/noieee_src/n_lgamma.c | 45 | ||||
-rw-r--r-- | lib/libm/noieee_src/n_log.c | 28 | ||||
-rw-r--r-- | lib/libm/noieee_src/n_tgamma.c (renamed from lib/libm/noieee_src/n_gamma.c) | 101 |
4 files changed, 123 insertions, 83 deletions
diff --git a/lib/libm/noieee_src/n_exp.c b/lib/libm/noieee_src/n_exp.c index e1fef84bc1b..a51696b58d6 100644 --- a/lib/libm/noieee_src/n_exp.c +++ b/lib/libm/noieee_src/n_exp.c @@ -1,4 +1,4 @@ -/* $NetBSD: n_exp.c,v 1.1 1995/10/10 23:36:44 ragge Exp $ */ +/* $OpenBSD: n_exp.c,v 1.5 2008/06/11 20:53:27 martynas Exp $ */ /* * Copyright (c) 1985, 1993 * The Regents of the University of California. All rights reserved. @@ -35,21 +35,21 @@ static char sccsid[] = "@(#)exp.c 8.1 (Berkeley) 6/4/93"; /* EXP(X) * RETURN THE EXPONENTIAL OF X * DOUBLE PRECISION (IEEE 53 bits, VAX D FORMAT 56 BITS) - * CODED IN C BY K.C. NG, 1/19/85; + * CODED IN C BY K.C. NG, 1/19/85; * REVISED BY K.C. NG on 2/6/85, 2/15/85, 3/7/85, 3/24/85, 4/16/85, 6/14/86. * * Required system supported functions: - * scalbn(x,n) - * copysign(x,y) + * scalbn(x,n) + * copysign(x,y) * finite(x) * * Method: - * 1. Argument Reduction: given the input x, find r and integer k such + * 1. Argument Reduction: given the input x, find r and integer k such * that - * x = k*ln2 + r, |r| <= 0.5*ln2 . + * x = k*ln2 + r, |r| <= 0.5*ln2 . * r will be represented as r := z+c for better accuracy. * - * 2. Compute exp(r) by + * 2. Compute exp(r) by * * exp(r) = 1 + r + r*R1/(2-R1), * where @@ -111,10 +111,9 @@ ic(lnhuge, 7.1602103751842355450E2, 9, 1.6602B15B7ECF2) ic(lntiny,-7.5137154372698068983E2, 9, -1.77AF8EBEAE354) ic(invln2, 1.4426950408889633870E0, 0, 1.71547652B82FE) -double exp(x) -double x; +double exp(double x) { - double z,hi,lo,c; + double z, hi, lo, c; int k; #if !defined(__vax__)&&!defined(tahoe) @@ -140,7 +139,7 @@ double x; } /* end of x > lntiny */ - else + else /* exp(-big#) underflows to zero */ if(finite(x)) return(scalbn(1.0,-5000)); @@ -149,17 +148,16 @@ double x; } /* end of x < lnhuge */ - else + else /* exp(INF) is INF, exp(+big#) overflows to INF */ return( finite(x) ? scalbn(1.0,5000) : x); } /* returns exp(r = x + c) for |c| < |x| with no overlap. */ -double __exp__D(x, c) -double x, c; +double __exp__D(double x, double c) { - double z,hi,lo, t; + double z, hi, lo; int k; #if !defined(__vax__)&&!defined(tahoe) @@ -185,7 +183,7 @@ double x, c; } /* end of x > lntiny */ - else + else /* exp(-big#) underflows to zero */ if(finite(x)) return(scalbn(1.0,-5000)); @@ -194,7 +192,7 @@ double x, c; } /* end of x < lnhuge */ - else + else /* exp(INF) is INF, exp(+big#) overflows to INF */ return( finite(x) ? scalbn(1.0,5000) : x); } diff --git a/lib/libm/noieee_src/n_lgamma.c b/lib/libm/noieee_src/n_lgamma.c index 5eaed3d2f34..c874e8c6ba5 100644 --- a/lib/libm/noieee_src/n_lgamma.c +++ b/lib/libm/noieee_src/n_lgamma.c @@ -1,4 +1,4 @@ -/* $NetBSD: n_lgamma.c,v 1.1 1995/10/10 23:36:56 ragge Exp $ */ +/* $OpenBSD: n_lgamma.c,v 1.4 2008/06/11 20:53:27 martynas Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -53,7 +53,7 @@ static char sccsid[] = "@(#)lgamma.c 8.2 (Berkeley) 11/30/93"; * x > 6: * Use the asymptotic expansion (Stirling's Formula) * 0 < x < 6: - * Use gamma(x+1) = x*gamma(x) for argument reduction. + * Use tgamma(x+1) = x*tgamma(x) for argument reduction. * Use rational approximation in * the range 1.2, 2.5 * Two approximations are used, one centered at the @@ -66,12 +66,12 @@ static char sccsid[] = "@(#)lgamma.c 8.2 (Berkeley) 11/30/93"; * non-positive integer returns +Inf. * NaN returns NaN */ -static int endian; #if defined(__vax__) || defined(tahoe) #define _IEEE 0 /* double and float have same size exponent field */ #define TRUNC(x) x = (double) (float) (x) #else +static int endian; #define _IEEE 1 #define TRUNC(x) *(((int *) &x) + endian) &= 0xf8000000 #define infnan(x) 0.0 @@ -80,16 +80,16 @@ static int endian; static double small_lgam(double); static double large_lgam(double); static double neg_lgam(double); -static double zero = 0.0, one = 1.0; -int signgam; +static const double one = 1.0; +extern int signgam; #define UNDERFL (1e-1020 * 1e-1020) #define LEFT (1.0 - (x0 + .25)) #define RIGHT (x0 - .218) /* -/* Constants for approximation in [1.244,1.712] -*/ + * Constants for approximation in [1.244,1.712] + */ #define x0 0.461632144968362356785 #define x0_lo -.000000000000000015522348162858676890521 #define a0_hi -0.12148629128932952880859 @@ -141,7 +141,9 @@ lgamma(double x) double r; signgam = 1; +#if _IEEE endian = ((*(int *) &one)) ? 1 : 0; +#endif if (!finite(x)) if (_IEEE) @@ -161,11 +163,33 @@ lgamma(double x) return (neg_lgam(x)); } +float +lgammaf(float x) +{ + return lgamma(x); +} + +/* + * The gamma() function performs identically to lgamma(), including + * the use of signgam. + */ + +double +gamma(double x) +{ + return lgamma(x); +} + +float +gammaf(float x) +{ + return lgammaf(x); +} + static double large_lgam(double x) { double z, p, x1; - int i; struct Double t, u, v; u = __log__D(x); u.a -= 1.0; @@ -266,8 +290,7 @@ static double neg_lgam(double x) { int xi; - double y, z, one = 1.0, zero = 0.0; - extern double gamma(); + double y, z, zero = 0.0; /* avoid destructive cancellation as much as possible */ if (x > -170) { @@ -277,7 +300,7 @@ neg_lgam(double x) return(one/zero); else return(infnan(ERANGE)); - y = gamma(x); + y = tgamma(x); if (y < 0) y = -y, signgam = -1; return (log(y)); diff --git a/lib/libm/noieee_src/n_log.c b/lib/libm/noieee_src/n_log.c index ac367f72429..04f4a22bacc 100644 --- a/lib/libm/noieee_src/n_log.c +++ b/lib/libm/noieee_src/n_log.c @@ -1,4 +1,4 @@ -/* $NetBSD: n_log.c,v 1.1 1995/10/10 23:36:57 ragge Exp $ */ +/* $OpenBSD: n_log.c,v 1.4 2008/06/11 20:53:27 martynas Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -93,12 +93,12 @@ static char sccsid[] = "@(#)log.c 8.2 (Berkeley) 11/30/93"; * Values for log(F) were generated using error < 10^-57 absolute * with the bc -l package. */ -static double A1 = .08333333333333178827; -static double A2 = .01250000000377174923; -static double A3 = .002232139987919447809; -static double A4 = .0004348877777076145742; +static const double A1 = .08333333333333178827; +static const double A2 = .01250000000377174923; +static const double A3 = .002232139987919447809; +static const double A4 = .0004348877777076145742; -static double logF_head[N+1] = { +static const double logF_head[N+1] = { 0., .007782140442060381246, .015504186535963526694, @@ -230,7 +230,7 @@ static double logF_head[N+1] = { .693147180560117703862 }; -static double logF_tail[N+1] = { +static const double logF_tail[N+1] = { 0., -.00000000000000543229938420049, .00000000000000172745674997061, @@ -363,11 +363,7 @@ static double logF_tail[N+1] = { }; double -#ifdef _ANSI_SOURCE log(double x) -#else -log(x) double x; -#endif { int m, j; double F, f, g, q, u, u2, v, zero = 0.0, one = 1.0; @@ -388,7 +384,7 @@ log(x) double x; return (x+x); else return (infnan(ERANGE)); - + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ /* y = F*(1 + f/F) for |f| <= 2^-8 */ @@ -435,17 +431,13 @@ log(x) double x; /* * Extra precision variant, returning struct {double a, b;}; - * log(x) = a+b to 63 bits, with a is rounded to 26 bits. + * log(x) = a+b to 63 bits, with a rounded to 26 bits. */ struct Double -#ifdef _ANSI_SOURCE __log__D(double x) -#else -__log__D(x) double x; -#endif { int m, j; - double F, f, g, q, u, v, u2, one = 1.0; + double F, f, g, q, u, v, u2; volatile double u1; struct Double r; diff --git a/lib/libm/noieee_src/n_gamma.c b/lib/libm/noieee_src/n_tgamma.c index cce478274c7..de9ff444733 100644 --- a/lib/libm/noieee_src/n_gamma.c +++ b/lib/libm/noieee_src/n_tgamma.c @@ -1,4 +1,4 @@ -/* $NetBSD: n_gamma.c,v 1.1 1995/10/10 23:36:50 ragge Exp $ */ +/* $OpenBSD: n_tgamma.c,v 1.1 2008/06/11 20:53:27 martynas Exp $ */ /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -45,7 +45,7 @@ static char sccsid[] = "@(#)gamma.c 8.1 (Berkeley) 6/4/93"; /* METHOD: * x < 0: Use reflection formula, G(x) = pi/(sin(pi*x)*x*G(x)) - * At negative integers, return +Inf, and set errno. + * At negative integers, return +Inf and raise overflow. * * x < 6.5: * Use argument reduction G(x+1) = xG(x) to reach the @@ -62,11 +62,15 @@ static char sccsid[] = "@(#)gamma.c 8.1 (Berkeley) 6/4/93"; * avoid premature round-off. * * Special values: - * non-positive integer: Set overflow trap; return +Inf; - * x > 171.63: Set overflow trap; return +Inf; - * NaN: Set invalid trap; return NaN + * -Inf: return 0 and raise invalid; + * negative integer: return +Inf and raise overflow; + * other x ~< -177.79: return 0 and raise underflow; + * +-0: return +Inf and raise overflow; + * finite x ~> 171.63: return +Inf and raise div-by-0; + * +Inf: return +Inf and raise div-by-0; + * NaN: return 0 and raise invalid. * - * Accuracy: Gamma(x) is accurate to within + * Accuracy: tgamma(x) is accurate to within * x > 0: error provably < 0.9ulp. * Maximum observed in 1,000,000 trials was .87ulp. * x < 0: @@ -118,7 +122,7 @@ static struct Double ratfun_gam(double, double); #define Pa7 -1.44705562421428915453880392761e-02 static const double zero = 0., one = 1.0, tiny = 1e-300; -static int endian; + /* * TRUNC sets trailing bits in a floating-point number to zero. * is a temporary variable. @@ -127,21 +131,26 @@ static int endian; #define _IEEE 0 #define TRUNC(x) x = (double) (float) (x) #else +static int endian; #define _IEEE 1 #define TRUNC(x) *(((int *) &x) + endian) &= 0xf8000000 #define infnan(x) 0.0 #endif double -gamma(x) - double x; +tgamma(double x) { struct Double u; +#if _IEEE endian = (*(int *) &one) ? 1 : 0; +#endif if (x >= 6) { if(x > 171.63) - return(one/zero); + if (_IEEE) + return (x/zero); + else + return (infnan(ERANGE)); u = large_gam(x); return(__exp__D(u.a, u.b)); } else if (x >= 1.0 + LEFT + x0) @@ -149,28 +158,43 @@ gamma(x) else if (x > 1.e-17) return (smaller_gam(x)); else if (x > -1.e-17) { - if (x == 0.0) - if (!_IEEE) return (infnan(ERANGE)); - else return (one/x); - one+1e-20; /* Raise inexact flag. */ + if (x == 0.0) { + if (!_IEEE) + return (infnan(ERANGE)); + } else { + u.a = one - tiny; /* raise inexact */ + } return (one/x); } else if (!finite(x)) { if (_IEEE) /* x = NaN, -Inf */ - return (x*x); + return (x - x); else return (infnan(EDOM)); } else return (neg_gam(x)); } + +/* + * We simply call tgamma() rather than bloating the math library + * with a float-optimized version of it. The reason is that tgammaf() + * is essentially useless, since the function is superexponential + * and floats have very limited range. -- das@freebsd.org + */ + +float +tgammaf(float x) +{ + return tgamma(x); +} + /* * Accurate to max(ulp(1/128) absolute, 2^-66 relative) error. */ + static struct Double -large_gam(x) - double x; +large_gam(double x) { double z, p; - int i; struct Double t, u, v; z = one/(x*x); @@ -191,15 +215,16 @@ large_gam(x) u.b += lns2pi_hi; u.b += t.b; return (u); } + /* * Good to < 1 ulp. (provably .90 ulp; .87 ulp on 1,000,000 runs.) * It also has correct monotonicity. */ + static double -small_gam(x) - double x; +small_gam(double x) { - double y, ym1, t, x1; + double y, ym1, t; struct Double yy, r; y = x - one; ym1 = y - one; @@ -220,18 +245,19 @@ small_gam(x) TRUNC(r.a); r.b += (t - r.a); } - /* Return r*gamma(y). */ + /* Return r*tgamma(y). */ yy = ratfun_gam(y - x0, 0); y = r.b*(yy.a + yy.b) + r.a*yy.b; y += yy.a*r.a; return (y); } + /* * Good on (0, 1+x0+LEFT]. Accurate to 1ulp. */ + static double -smaller_gam(x) - double x; +smaller_gam(double x) { double t, d; struct Double r, xx; @@ -255,14 +281,14 @@ smaller_gam(x) r.a -= d*xx.a; r.a -= d*xx.b; r.a += r.b; return (d + r.a/x); } + /* * returns (z+c)^2 * P(z)/Q(z) + a0 */ + static struct Double -ratfun_gam(z, c) - double z, c; +ratfun_gam(double z, double c) { - int i; double p, q; struct Double r, t; @@ -287,21 +313,22 @@ ratfun_gam(z, c) } static double -neg_gam(x) - double x; +neg_gam(double x) { int sgn = 1; struct Double lg, lsine; double y, z; - y = floor(x + .5); + y = ceil(x); if (y == x) /* Negative integer. */ - if(!_IEEE) - return (infnan(ERANGE)); + if (_IEEE) + return ((x - x) / zero); else - return (one/zero); - z = fabs(x - y); - y = .5*ceil(x); + return (infnan(ERANGE)); + z = y - x; + if (z > 0.5) + z = one - z; + y = 0.5 * y; if (y == ceil(y)) sgn = -1; if (z < .25) @@ -325,9 +352,9 @@ neg_gam(x) } y = one-x; if (one-y == x) - y = gamma(y); + y = tgamma(y); else /* 1-x is inexact */ - y = -x*gamma(-x); + y = -x*tgamma(-x); if (sgn < 0) y = -y; return (M_PI / (y*z)); } |