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 | |
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')
-rw-r--r-- | lib/libm/Makefile | 11 | ||||
-rw-r--r-- | lib/libm/man/lgamma.3 | 55 | ||||
-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 | ||||
-rw-r--r-- | lib/libm/shlib_version | 2 | ||||
-rw-r--r-- | lib/libm/src/b_exp__D.c | 126 | ||||
-rw-r--r-- | lib/libm/src/b_log__D.c | 396 | ||||
-rw-r--r-- | lib/libm/src/b_tgamma.c | 329 | ||||
-rw-r--r-- | lib/libm/src/math_private.h | 69 |
11 files changed, 1082 insertions, 112 deletions
diff --git a/lib/libm/Makefile b/lib/libm/Makefile index d8d787b3e40..e5d085f4b43 100644 --- a/lib/libm/Makefile +++ b/lib/libm/Makefile @@ -1,5 +1,5 @@ # $NetBSD: Makefile,v 1.28 1995/11/20 22:06:19 jtc Exp $ -# $OpenBSD: Makefile,v 1.43 2008/06/11 14:50:06 martynas Exp $ +# $OpenBSD: Makefile,v 1.44 2008/06/11 20:53:27 martynas Exp $ # # @(#)Makefile 5.1beta 93/09/24 # @@ -100,7 +100,8 @@ CFLAGS+= -D_MULTI_LIBM -D_POSIX_MODE LIB= m WANTLINT= -COMMON_SRCS = e_acos.c e_acosf.c e_acosh.c e_acoshf.c e_asin.c e_asinf.c \ +COMMON_SRCS = b_exp__D.c b_log__D.c b_tgamma.c \ + e_acos.c e_acosf.c e_acosh.c e_acoshf.c e_asin.c e_asinf.c \ e_atan2.c e_atan2f.c e_atanh.c e_atanhf.c e_cosh.c e_coshf.c e_exp.c \ e_expf.c e_fmod.c e_fmodf.c e_hypot.c e_hypotf.c e_j0.c e_j0f.c \ e_j1.c e_j1f.c e_jn.c e_jnf.c e_lgamma_r.c e_lgammaf_r.c e_log.c \ @@ -131,12 +132,12 @@ COMMON_SRCS = e_acos.c e_acosf.c e_acosh.c e_acoshf.c e_asin.c e_asinf.c \ # math routines for non-IEEE architectures. NOIEEE_SRCS = n_asincos.c n_acosh.c n_asinh.c n_atan.c n_atanh.c n_cosh.c \ - n_erf.c n_exp.c n_exp__E.c n_expm1.c n_floor.c n_fmod.c n_gamma.c \ + n_erf.c n_exp.c n_exp__E.c n_expm1.c n_floor.c n_fmod.c n_tgamma.c \ n_lgamma.c n_j0.c n_j1.c n_jn.c n_log.c n_log10.c n_log1p.c \ n_log__L.c n_pow.c n_sinh.c n_tanh.c n_atan2.c n_cabs.c n_cbrt.c \ n_sqrt.c n_sincos.c n_tan.c n_argred.c n_support.c n_infnan.c \ n_round.c - + # NetBSD's C library supplies these functions: #COMMON_SRCS+= s_fabs.c s_frexp.c s_isinf.c s_isnan.c s_ldexp.c s_modf.c @@ -173,7 +174,7 @@ MLINKS+=ieee.3 copysign.3 ieee.3 drem.3 ieee.3 finite.3 ieee.3 ilogb.3 \ MLINKS+=logb.3 scalb.3 MLINKS+=logb.3 significand.3 MLINKS+=j0.3 j1.3 j0.3 jn.3 j0.3 y0.3 j0.3 y1.3 j0.3 yn.3 -MLINKS+=lgamma.3 gamma.3 +MLINKS+=lgamma.3 gamma.3 lgamma.3 tgamma.3 lgamma.3 tgammaf.3 MLINKS+=sqrt.3 cbrt.3 # float versions diff --git a/lib/libm/man/lgamma.3 b/lib/libm/man/lgamma.3 index cc3f7411cdf..af12f5c50e6 100644 --- a/lib/libm/man/lgamma.3 +++ b/lib/libm/man/lgamma.3 @@ -1,4 +1,4 @@ -.\" $OpenBSD: lgamma.3,v 1.14 2007/05/31 19:19:35 jmc Exp $ +.\" $OpenBSD: lgamma.3,v 1.15 2008/06/11 20:53:27 martynas Exp $ .\" Copyright (c) 1985, 1991 Regents of the University of California. .\" All rights reserved. .\" @@ -28,12 +28,14 @@ .\" .\" from: @(#)lgamma.3 6.6 (Berkeley) 12/3/92 .\" -.Dd $Mdocdate: May 31 2007 $ +.Dd $Mdocdate: June 11 2008 $ .Dt LGAMMA 3 .Os .Sh NAME .Nm lgamma , -.Nm lgammaf +.Nm lgammaf , +.Nm tgamma , +.Nm tgammaf .Nd log gamma functions .Sh SYNOPSIS .Fd #include <math.h> @@ -44,6 +46,10 @@ .Fn lgamma "double x" .Ft float .Fn lgammaf "float x" +.Ft double +.Fn tgamma "double x" +.Ft float +.Fn tgammaf "float x" .Sh DESCRIPTION .Fn lgamma x .if t \{\ @@ -64,6 +70,23 @@ The .Fn lgammaf function is a single precision version of .Fn lgamma . +.Pp +The +.Fn tgamma x +and +.Fn tgammaf x +functions return \(*G(x), with no effect on +.Fa signgam . +.Pp +The +.Fn gamma , +and +.Fn gammaf +are deprecated aliases for +.Fn lgamma , +and +.Fn lgammaf +respectively. .Sh IDIOSYNCRASIES Do not use the expression .Sq Li signgam\(**exp(lgamma(x)) @@ -76,11 +99,19 @@ lg = lgamma(x); g = signgam\(**exp(lg); Only after .Fn lgamma has returned can signgam be correct. +.Pp +For arguments in its range, +.Fn tgamma +is preferred, as for positive arguments +it is accurate to within one unit in the last place. .Sh RETURN VALUES .Fn lgamma returns appropriate values unless an argument is out of range. Overflow will occur for sufficiently large positive values, and non-positive integers. +For large non-integer negative values, +.Fn tgamma +will underflow. On the .Tn VAX , the reserved operator is returned, @@ -91,8 +122,26 @@ is set to .Sh SEE ALSO .Xr infnan 3 , .Xr math 3 +.Sh STANDARDS +The +.Fn lgamma , +.Fn lgammaf , +.Fn tgamma , +and +.Fn tgammaf +functions are expected to conform to +.St -isoC-99 . .Sh HISTORY The .Fn lgamma function appeared in .Bx 4.3 . +The +.Fn tgamma +function appeared in +.Ox 4.4 , +which is based on the +.Fn gamma +function that appeared in +.Bx 4.4 +as a function to compute \(*G(x). 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)); } diff --git a/lib/libm/shlib_version b/lib/libm/shlib_version index b363be4447c..c87e1c60d46 100644 --- a/lib/libm/shlib_version +++ b/lib/libm/shlib_version @@ -1,2 +1,2 @@ major=2 -minor=3 +minor=4 diff --git a/lib/libm/src/b_exp__D.c b/lib/libm/src/b_exp__D.c new file mode 100644 index 00000000000..18f4452d7d5 --- /dev/null +++ b/lib/libm/src/b_exp__D.c @@ -0,0 +1,126 @@ +/* $OpenBSD: b_exp__D.c,v 1.1 2008/06/11 20:53:27 martynas Exp $ */ +/* + * Copyright (c) 1985, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exp.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* 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; + * 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: + * scalb(x,n) + * copysign(x,y) + * finite(x) + * + * Method: + * 1. Argument Reduction: given the input x, find r and integer k such + * that + * x = k*ln2 + r, |r| <= 0.5*ln2 . + * r will be represented as r := z+c for better accuracy. + * + * 2. Compute exp(r) by + * + * exp(r) = 1 + r + r*R1/(2-R1), + * where + * R1 = x - x^2*(p1+x^2*(p2+x^2*(p3+x^2*(p4+p5*x^2)))). + * + * 3. exp(x) = 2^k * exp(r) . + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF)= 0; + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * exp(x) returns the exponential of x nearly rounded. In a test run + * with 1,156,000 random arguments on a VAX, the maximum observed + * error was 0.869 ulps (units in the last place). + */ + +#include "math.h" +#include "math_private.h" + +const static double p1 = 0x1.555555555553ep-3; +const static double p2 = -0x1.6c16c16bebd93p-9; +const static double p3 = 0x1.1566aaf25de2cp-14; +const static double p4 = -0x1.bbd41c5d26bf1p-20; +const static double p5 = 0x1.6376972bea4d0p-25; +const static double ln2hi = 0x1.62e42fee00000p-1; +const static double ln2lo = 0x1.a39ef35793c76p-33; +const static double lnhuge = 0x1.6602b15b7ecf2p9; +const static double lntiny = -0x1.77af8ebeae354p9; +const static double invln2 = 0x1.71547652b82fep0; + +/* returns exp(r = x + c) for |c| < |x| with no overlap. */ + +double __exp__D(double x, double c) +{ + double z, hi, lo; + int k; + + if (x != x) /* x is NaN */ + return(x); + if ( x <= lnhuge ) { + if ( x >= lntiny ) { + + /* argument reduction : x --> x - k*ln2 */ + z = invln2*x; + k = z + copysign(.5, x); + + /* express (x+c)-k*ln2 as hi-lo and let x=hi-lo rounded */ + + hi=(x-k*ln2hi); /* Exact. */ + x= hi - (lo = k*ln2lo-c); + /* return 2^k*[1+x+x*c/(2+c)] */ + z=x*x; + c= x - z*(p1+z*(p2+z*(p3+z*(p4+z*p5)))); + c = (x*c)/(2.0-c); + + return scalb(1.+(hi-(lo - c)), k); + } + /* end of x > lntiny */ + + else + /* exp(-big#) underflows to zero */ + if(finite(x)) return(scalb(1.0,-5000)); + + /* exp(-INF) is zero */ + else return(0.0); + } + /* end of x < lnhuge */ + + else + /* exp(INF) is INF, exp(+big#) overflows to INF */ + return( finite(x) ? scalb(1.0,5000) : x); +} diff --git a/lib/libm/src/b_log__D.c b/lib/libm/src/b_log__D.c new file mode 100644 index 00000000000..4c862a7b9d4 --- /dev/null +++ b/lib/libm/src/b_log__D.c @@ -0,0 +1,396 @@ +/* $OpenBSD: b_log__D.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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)log.c 8.2 (Berkeley) 11/30/93"; +#endif /* not lint */ + +#include "math.h" +#include "math_private.h" + +/* Table-driven natural logarithm. + * + * This code was derived, with minor modifications, from: + * Peter Tang, "Table-Driven Implementation of the + * Logarithm in IEEE Floating-Point arithmetic." ACM Trans. + * Math Software, vol 16. no 4, pp 378-400, Dec 1990). + * + * Calculates log(2^m*F*(1+f/F)), |f/j| <= 1/256, + * where F = j/128 for j an integer in [0, 128]. + * + * log(2^m) = log2_hi*m + log2_tail*m + * since m is an integer, the dominant term is exact. + * m has at most 10 digits (for subnormal numbers), + * and log2_hi has 11 trailing zero bits. + * + * log(F) = logF_hi[j] + logF_lo[j] is in tabular form in log_table.h + * logF_hi[] + 512 is exact. + * + * log(1+f/F) = 2*f/(2*F + f) + 1/12 * (2*f/(2*F + f))**3 + ... + * the leading term is calculated to extra precision in two + * parts, the larger of which adds exactly to the dominant + * m and F terms. + * There are two cases: + * 1. when m, j are non-zero (m | j), use absolute + * precision for the leading term. + * 2. when m = j = 0, |1-x| < 1/256, and log(x) ~= (x-1). + * In this case, use a relative precision of 24 bits. + * (This is done differently in the original paper) + * + * Special cases: + * 0 return signalling -Inf + * neg return signalling NaN + * +Inf return +Inf +*/ + +#define N 128 + +/* Table of log(Fj) = logF_head[j] + logF_tail[j], for Fj = 1+j/128. + * Used for generation of extend precision logarithms. + * The constant 35184372088832 is 2^45, so the divide is exact. + * It ensures correct reading of logF_head, even for inaccurate + * decimal-to-binary conversion routines. (Everybody gets the + * right answer for integers less than 2^53.) + * Values for log(F) were generated using error < 10^-57 absolute + * with the bc -l package. +*/ +static const double A1 = .08333333333333178827; +static const double A2 = .01250000000377174923; +static const double A3 = .002232139987919447809; +static const double A4 = .0004348877777076145742; + +static const double logF_head[N+1] = { + 0., + .007782140442060381246, + .015504186535963526694, + .023167059281547608406, + .030771658666765233647, + .038318864302141264488, + .045809536031242714670, + .053244514518837604555, + .060624621816486978786, + .067950661908525944454, + .075223421237524235039, + .082443669210988446138, + .089612158689760690322, + .096729626458454731618, + .103796793681567578460, + .110814366340264314203, + .117783035656430001836, + .124703478501032805070, + .131576357788617315236, + .138402322859292326029, + .145182009844575077295, + .151916042025732167530, + .158605030176659056451, + .165249572895390883786, + .171850256926518341060, + .178407657472689606947, + .184922338493834104156, + .191394852999565046047, + .197825743329758552135, + .204215541428766300668, + .210564769107350002741, + .216873938300523150246, + .223143551314024080056, + .229374101064877322642, + .235566071312860003672, + .241719936886966024758, + .247836163904594286577, + .253915209980732470285, + .259957524436686071567, + .265963548496984003577, + .271933715484010463114, + .277868451003087102435, + .283768173130738432519, + .289633292582948342896, + .295464212893421063199, + .301261330578199704177, + .307025035294827830512, + .312755710004239517729, + .318453731118097493890, + .324119468654316733591, + .329753286372579168528, + .335355541920762334484, + .340926586970454081892, + .346466767346100823488, + .351976423156884266063, + .357455888922231679316, + .362905493689140712376, + .368325561158599157352, + .373716409793814818840, + .379078352934811846353, + .384411698910298582632, + .389716751140440464951, + .394993808240542421117, + .400243164127459749579, + .405465108107819105498, + .410659924985338875558, + .415827895143593195825, + .420969294644237379543, + .426084395310681429691, + .431173464818130014464, + .436236766774527495726, + .441274560805140936281, + .446287102628048160113, + .451274644139630254358, + .456237433481874177232, + .461175715122408291790, + .466089729924533457960, + .470979715219073113985, + .475845904869856894947, + .480688529345570714212, + .485507815781602403149, + .490303988045525329653, + .495077266798034543171, + .499827869556611403822, + .504556010751912253908, + .509261901790523552335, + .513945751101346104405, + .518607764208354637958, + .523248143765158602036, + .527867089620485785417, + .532464798869114019908, + .537041465897345915436, + .541597282432121573947, + .546132437597407260909, + .550647117952394182793, + .555141507540611200965, + .559615787935399566777, + .564070138285387656651, + .568504735352689749561, + .572919753562018740922, + .577315365035246941260, + .581691739635061821900, + .586049045003164792433, + .590387446602107957005, + .594707107746216934174, + .599008189645246602594, + .603290851438941899687, + .607555250224322662688, + .611801541106615331955, + .616029877215623855590, + .620240409751204424537, + .624433288012369303032, + .628608659422752680256, + .632766669570628437213, + .636907462236194987781, + .641031179420679109171, + .645137961373620782978, + .649227946625615004450, + .653301272011958644725, + .657358072709030238911, + .661398482245203922502, + .665422632544505177065, + .669430653942981734871, + .673422675212350441142, + .677398823590920073911, + .681359224807238206267, + .685304003098281100392, + .689233281238557538017, + .693147180560117703862 +}; + +static const double logF_tail[N+1] = { + 0., + -.00000000000000543229938420049, + .00000000000000172745674997061, + -.00000000000001323017818229233, + -.00000000000001154527628289872, + -.00000000000000466529469958300, + .00000000000005148849572685810, + -.00000000000002532168943117445, + -.00000000000005213620639136504, + -.00000000000001819506003016881, + .00000000000006329065958724544, + .00000000000008614512936087814, + -.00000000000007355770219435028, + .00000000000009638067658552277, + .00000000000007598636597194141, + .00000000000002579999128306990, + -.00000000000004654729747598444, + -.00000000000007556920687451336, + .00000000000010195735223708472, + -.00000000000017319034406422306, + -.00000000000007718001336828098, + .00000000000010980754099855238, + -.00000000000002047235780046195, + -.00000000000008372091099235912, + .00000000000014088127937111135, + .00000000000012869017157588257, + .00000000000017788850778198106, + .00000000000006440856150696891, + .00000000000016132822667240822, + -.00000000000007540916511956188, + -.00000000000000036507188831790, + .00000000000009120937249914984, + .00000000000018567570959796010, + -.00000000000003149265065191483, + -.00000000000009309459495196889, + .00000000000017914338601329117, + -.00000000000001302979717330866, + .00000000000023097385217586939, + .00000000000023999540484211737, + .00000000000015393776174455408, + -.00000000000036870428315837678, + .00000000000036920375082080089, + -.00000000000009383417223663699, + .00000000000009433398189512690, + .00000000000041481318704258568, + -.00000000000003792316480209314, + .00000000000008403156304792424, + -.00000000000034262934348285429, + .00000000000043712191957429145, + -.00000000000010475750058776541, + -.00000000000011118671389559323, + .00000000000037549577257259853, + .00000000000013912841212197565, + .00000000000010775743037572640, + .00000000000029391859187648000, + -.00000000000042790509060060774, + .00000000000022774076114039555, + .00000000000010849569622967912, + -.00000000000023073801945705758, + .00000000000015761203773969435, + .00000000000003345710269544082, + -.00000000000041525158063436123, + .00000000000032655698896907146, + -.00000000000044704265010452446, + .00000000000034527647952039772, + -.00000000000007048962392109746, + .00000000000011776978751369214, + -.00000000000010774341461609578, + .00000000000021863343293215910, + .00000000000024132639491333131, + .00000000000039057462209830700, + -.00000000000026570679203560751, + .00000000000037135141919592021, + -.00000000000017166921336082431, + -.00000000000028658285157914353, + -.00000000000023812542263446809, + .00000000000006576659768580062, + -.00000000000028210143846181267, + .00000000000010701931762114254, + .00000000000018119346366441110, + .00000000000009840465278232627, + -.00000000000033149150282752542, + -.00000000000018302857356041668, + -.00000000000016207400156744949, + .00000000000048303314949553201, + -.00000000000071560553172382115, + .00000000000088821239518571855, + -.00000000000030900580513238244, + -.00000000000061076551972851496, + .00000000000035659969663347830, + .00000000000035782396591276383, + -.00000000000046226087001544578, + .00000000000062279762917225156, + .00000000000072838947272065741, + .00000000000026809646615211673, + -.00000000000010960825046059278, + .00000000000002311949383800537, + -.00000000000058469058005299247, + -.00000000000002103748251144494, + -.00000000000023323182945587408, + -.00000000000042333694288141916, + -.00000000000043933937969737844, + .00000000000041341647073835565, + .00000000000006841763641591466, + .00000000000047585534004430641, + .00000000000083679678674757695, + -.00000000000085763734646658640, + .00000000000021913281229340092, + -.00000000000062242842536431148, + -.00000000000010983594325438430, + .00000000000065310431377633651, + -.00000000000047580199021710769, + -.00000000000037854251265457040, + .00000000000040939233218678664, + .00000000000087424383914858291, + .00000000000025218188456842882, + -.00000000000003608131360422557, + -.00000000000050518555924280902, + .00000000000078699403323355317, + -.00000000000067020876961949060, + .00000000000016108575753932458, + .00000000000058527188436251509, + -.00000000000035246757297904791, + -.00000000000018372084495629058, + .00000000000088606689813494916, + .00000000000066486268071468700, + .00000000000063831615170646519, + .00000000000025144230728376072, + -.00000000000017239444525614834 +}; + +/* + * Extra precision variant, returning struct {double a, b;}; + * log(x) = a+b to 63 bits, with a rounded to 26 bits. + */ +struct Double +__log__D(double x) +{ + int m, j; + double F, f, g, q, u, v, u2; + volatile double u1; + struct Double r; + + /* Argument reduction: 1 <= g < 2; x/2^m = g; */ + /* y = F*(1 + f/F) for |f| <= 2^-8 */ + + m = logb(x); + g = ldexp(x, -m); + if (m == -1022) { + j = logb(g), m += j; + g = ldexp(g, -j); + } + j = N*(g-1) + .5; + F = (1.0/N) * j + 1; + f = g - F; + + g = 1/(2*F+f); + u = 2*f*g; + v = u*u; + q = u*v*(A1 + v*(A2 + v*(A3 + v*A4))); + if (m | j) + u1 = u + 513, u1 -= 513; + else + u1 = u, TRUNC(u1); + u2 = (2.0*(f - F*u1) - u1*f) * g; + + u1 += m*logF_head[N] + logF_head[j]; + + u2 += logF_tail[j]; u2 += q; + u2 += logF_tail[N]*m; + r.a = u1 + u2; /* Only difference is here */ + TRUNC(r.a); + r.b = (u1 - r.a) + u2; + return (r); +} diff --git a/lib/libm/src/b_tgamma.c b/lib/libm/src/b_tgamma.c new file mode 100644 index 00000000000..74b36085474 --- /dev/null +++ b/lib/libm/src/b_tgamma.c @@ -0,0 +1,329 @@ +/* $OpenBSD: b_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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)gamma.c 8.1 (Berkeley) 6/4/93"; +#endif /* not lint */ + +/* + * This code by P. McIlroy, Oct 1992; + * + * The financial support of UUNET Communications Services is greatfully + * acknowledged. + */ + +#include "math.h" +#include "math_private.h" + +/* METHOD: + * x < 0: Use reflection formula, G(x) = pi/(sin(pi*x)*x*G(x)) + * At negative integers, return NaN and raise invalid. + * + * x < 6.5: + * Use argument reduction G(x+1) = xG(x) to reach the + * range [1.066124,2.066124]. Use a rational + * approximation centered at the minimum (x0+1) to + * ensure monotonicity. + * + * x >= 6.5: Use the asymptotic approximation (Stirling's formula) + * adjusted for equal-ripples: + * + * log(G(x)) ~= (x-.5)*(log(x)-1) + .5(log(2*pi)-1) + 1/x*P(1/(x*x)) + * + * Keep extra precision in multiplying (x-.5)(log(x)-1), to + * avoid premature round-off. + * + * Special values: + * -Inf: return NaN and raise invalid; + * negative integer: return NaN and raise invalid; + * other x ~< -177.79: return +-0 and raise underflow; + * +-0: return +-Inf and raise divide-by-zero; + * finite x ~> 171.63: return +Inf and raise overflow; + * +Inf: return +Inf; + * NaN: return NaN. + * + * Accuracy: tgamma(x) is accurate to within + * x > 0: error provably < 0.9ulp. + * Maximum observed in 1,000,000 trials was .87ulp. + * x < 0: + * Maximum observed error < 4ulp in 1,000,000 trials. + */ + +static double neg_gam(double); +static double small_gam(double); +static double smaller_gam(double); +static struct Double large_gam(double); +static struct Double ratfun_gam(double, double); + +/* + * Rational approximation, A0 + x*x*P(x)/Q(x), on the interval + * [1.066.., 2.066..] accurate to 4.25e-19. + */ +#define LEFT -.3955078125 /* left boundary for rat. approx */ +#define x0 .461632144968362356785 /* xmin - 1 */ + +#define a0_hi 0.88560319441088874992 +#define a0_lo -.00000000000000004996427036469019695 +#define P0 6.21389571821820863029017800727e-01 +#define P1 2.65757198651533466104979197553e-01 +#define P2 5.53859446429917461063308081748e-03 +#define P3 1.38456698304096573887145282811e-03 +#define P4 2.40659950032711365819348969808e-03 +#define Q0 1.45019531250000000000000000000e+00 +#define Q1 1.06258521948016171343454061571e+00 +#define Q2 -2.07474561943859936441469926649e-01 +#define Q3 -1.46734131782005422506287573015e-01 +#define Q4 3.07878176156175520361557573779e-02 +#define Q5 5.12449347980666221336054633184e-03 +#define Q6 -1.76012741431666995019222898833e-03 +#define Q7 9.35021023573788935372153030556e-05 +#define Q8 6.13275507472443958924745652239e-06 +/* + * Constants for large x approximation (x in [6, Inf]) + * (Accurate to 2.8*10^-19 absolute) + */ +#define lns2pi_hi 0.418945312500000 +#define lns2pi_lo -.000006779295327258219670263595 +#define Pa0 8.33333333333333148296162562474e-02 +#define Pa1 -2.77777777774548123579378966497e-03 +#define Pa2 7.93650778754435631476282786423e-04 +#define Pa3 -5.95235082566672847950717262222e-04 +#define Pa4 8.41428560346653702135821806252e-04 +#define Pa5 -1.89773526463879200348872089421e-03 +#define Pa6 5.69394463439411649408050664078e-03 +#define Pa7 -1.44705562421428915453880392761e-02 + +static const double zero = 0., one = 1.0, tiny = 1e-300; + +double +tgamma(double x) +{ + struct Double u; + + if (x >= 6) { + if(x > 171.63) + return(x/zero); + u = large_gam(x); + return(__exp__D(u.a, u.b)); + } else if (x >= 1.0 + LEFT + x0) + return (small_gam(x)); + else if (x > 1.e-17) + return (smaller_gam(x)); + else if (x > -1.e-17) { + if (x != 0.0) + u.a = one - tiny; /* raise inexact */ + return (one/x); + } else if (!finite(x)) { + return (x - x); /* x = NaN, -Inf */ + } 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(double x) +{ + double z, p; + struct Double t, u, v; + + z = one/(x*x); + p = Pa0+z*(Pa1+z*(Pa2+z*(Pa3+z*(Pa4+z*(Pa5+z*(Pa6+z*Pa7)))))); + p = p/x; + + u = __log__D(x); + u.a -= one; + v.a = (x -= .5); + TRUNC(v.a); + v.b = x - v.a; + t.a = v.a*u.a; /* t = (x-.5)*(log(x)-1) */ + t.b = v.b*u.a + x*u.b; + /* return t.a + t.b + lns2pi_hi + lns2pi_lo + p */ + t.b += lns2pi_lo; t.b += p; + u.a = lns2pi_hi + t.b; u.a += t.a; + u.b = t.a - u.a; + 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(double x) +{ + double y, ym1, t; + struct Double yy, r; + y = x - one; + ym1 = y - one; + if (y <= 1.0 + (LEFT + x0)) { + yy = ratfun_gam(y - x0, 0); + return (yy.a + yy.b); + } + r.a = y; + TRUNC(r.a); + yy.a = r.a - one; + y = ym1; + yy.b = r.b = y - yy.a; + /* Argument reduction: G(x+1) = x*G(x) */ + for (ym1 = y-one; ym1 > LEFT + x0; y = ym1--, yy.a--) { + t = r.a*yy.a; + r.b = r.a*yy.b + y*r.b; + r.a = t; + TRUNC(r.a); + r.b += (t - r.a); + } + /* 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(double x) +{ + double t, d; + struct Double r, xx; + if (x < x0 + LEFT) { + t = x, TRUNC(t); + d = (t+x)*(x-t); + t *= t; + xx.a = (t + x), TRUNC(xx.a); + xx.b = x - xx.a; xx.b += t; xx.b += d; + t = (one-x0); t += x; + d = (one-x0); d -= t; d += x; + x = xx.a + xx.b; + } else { + xx.a = x, TRUNC(xx.a); + xx.b = x - xx.a; + t = x - x0; + d = (-x0 -t); d += x; + } + r = ratfun_gam(t, d); + d = r.a/x, TRUNC(d); + 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(double z, double c) +{ + double p, q; + struct Double r, t; + + q = Q0 +z*(Q1+z*(Q2+z*(Q3+z*(Q4+z*(Q5+z*(Q6+z*(Q7+z*Q8))))))); + p = P0 + z*(P1 + z*(P2 + z*(P3 + z*P4))); + + /* return r.a + r.b = a0 + (z+c)^2*p/q, with r.a truncated to 26 bits. */ + p = p/q; + t.a = z, TRUNC(t.a); /* t ~= z + c */ + t.b = (z - t.a) + c; + t.b *= (t.a + z); + q = (t.a *= t.a); /* t = (z+c)^2 */ + TRUNC(t.a); + t.b += (q - t.a); + r.a = p, TRUNC(r.a); /* r = P/Q */ + r.b = p - r.a; + t.b = t.b*p + t.a*r.b + a0_lo; + t.a *= r.a; /* t = (z+c)^2*(P/Q) */ + r.a = t.a + a0_hi, TRUNC(r.a); + r.b = ((a0_hi-r.a) + t.a) + t.b; + return (r); /* r = a0 + t */ +} + +static double +neg_gam(double x) +{ + int sgn = 1; + struct Double lg, lsine; + double y, z; + + y = ceil(x); + if (y == x) /* Negative integer. */ + return ((x - x) / zero); + z = y - x; + if (z > 0.5) + z = one - z; + y = 0.5 * y; + if (y == ceil(y)) + sgn = -1; + if (z < .25) + z = sin(M_PI*z); + else + z = cos(M_PI*(0.5-z)); + /* Special case: G(1-x) = Inf; G(x) may be nonzero. */ + if (x < -170) { + if (x < -190) + return ((double)sgn*tiny*tiny); + y = one - x; /* exact: 128 < |x| < 255 */ + lg = large_gam(y); + lsine = __log__D(M_PI/z); /* = TRUNC(log(u)) + small */ + lg.a -= lsine.a; /* exact (opposite signs) */ + lg.b -= lsine.b; + y = -(lg.a + lg.b); + z = (y + lg.a) + lg.b; + y = __exp__D(y, z); + if (sgn < 0) y = -y; + return (y); + } + y = one-x; + if (one-y == x) + y = tgamma(y); + else /* 1-x is inexact */ + y = -x*tgamma(-x); + if (sgn < 0) y = -y; + return (M_PI / (y*z)); +} diff --git a/lib/libm/src/math_private.h b/lib/libm/src/math_private.h index 2d2c7d8f2c3..59382ba17e4 100644 --- a/lib/libm/src/math_private.h +++ b/lib/libm/src/math_private.h @@ -1,4 +1,4 @@ -/* $OpenBSD: math_private.h,v 1.7 2007/06/01 05:50:56 jason Exp $ */ +/* $OpenBSD: math_private.h,v 1.8 2008/06/11 20:53:27 martynas Exp $ */ /* * ==================================================== * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. @@ -40,10 +40,10 @@ #if (BYTE_ORDER == BIG_ENDIAN) || defined(arm32) -typedef union +typedef union { double value; - struct + struct { u_int32_t msw; u_int32_t lsw; @@ -75,10 +75,10 @@ typedef union #if (BYTE_ORDER == LITTLE_ENDIAN) && !defined(arm32) -typedef union +typedef union { double value; - struct + struct { u_int32_t lsw; u_int32_t msw; @@ -194,13 +194,13 @@ do { \ } while (0) /* ieee style elementary functions */ -extern double __ieee754_sqrt(double); -extern double __ieee754_acos(double); -extern double __ieee754_acosh(double); -extern double __ieee754_log(double); -extern double __ieee754_atanh(double); -extern double __ieee754_asin(double); -extern double __ieee754_atan2(double,double); +extern double __ieee754_sqrt(double); +extern double __ieee754_acos(double); +extern double __ieee754_acosh(double); +extern double __ieee754_log(double); +extern double __ieee754_atanh(double); +extern double __ieee754_asin(double); +extern double __ieee754_atan2(double,double); extern double __ieee754_exp(double); extern double __ieee754_cosh(double); extern double __ieee754_fmod(double,double); @@ -223,7 +223,7 @@ extern int __ieee754_rem_pio2(double,double*); extern double __ieee754_scalb(double,double); /* fdlibm kernel function */ -extern double __kernel_standard(double,double,int); +extern double __kernel_standard(double,double,int); extern double __kernel_sin(double,double,int); extern double __kernel_cos(double,double); extern double __kernel_tan(double,double,int); @@ -231,13 +231,13 @@ extern int __kernel_rem_pio2(double*,double*,int,int,int,const int*); /* ieee style elementary float functions */ -extern float __ieee754_sqrtf(float); -extern float __ieee754_acosf(float); -extern float __ieee754_acoshf(float); -extern float __ieee754_logf(float); -extern float __ieee754_atanhf(float); -extern float __ieee754_asinf(float); -extern float __ieee754_atan2f(float,float); +extern float __ieee754_sqrtf(float); +extern float __ieee754_acosf(float); +extern float __ieee754_acoshf(float); +extern float __ieee754_logf(float); +extern float __ieee754_atanhf(float); +extern float __ieee754_asinf(float); +extern float __ieee754_atan2f(float,float); extern float __ieee754_expf(float); extern float __ieee754_coshf(float); extern float __ieee754_fmodf(float,float); @@ -265,4 +265,33 @@ extern float __kernel_cosf(float,float); extern float __kernel_tanf(float,float,int); extern int __kernel_rem_pio2f(float*,float*,int,int,int,const int*); +/* + * TRUNC() is a macro that sets the trailing 27 bits in the mantissa + * of an IEEE double variable to zero. It must be expression-like + * for syntactic reasons, and we implement this expression using + * an inline function instead of a pure macro to avoid depending + * on the gcc feature of statement-expressions. + */ +#define TRUNC(d) (_b_trunc(&(d))) + +static __inline void +_b_trunc(volatile double *_dp) +{ + uint32_t _lw; + + GET_LOW_WORD(_lw, *_dp); + SET_LOW_WORD(*_dp, _lw & 0xf8000000); +} + +struct Double { + double a; + double b; +}; + +/* + * Functions internal to the math package, yet not static. + */ +double __exp__D(double, double); +struct Double __log__D(double); + #endif /* _MATH_PRIVATE_H_ */ |