...

Source file src/math/lgamma.go

Documentation: math

		 1  // Copyright 2010 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  package math
		 6  
		 7  /*
		 8  	Floating-point logarithm of the Gamma function.
		 9  */
		10  
		11  // The original C code and the long comment below are
		12  // from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and
		13  // came with this notice. The go code is a simplified
		14  // version of the original C.
		15  //
		16  // ====================================================
		17  // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
		18  //
		19  // Developed at SunPro, a Sun Microsystems, Inc. business.
		20  // Permission to use, copy, modify, and distribute this
		21  // software is freely granted, provided that this notice
		22  // is preserved.
		23  // ====================================================
		24  //
		25  // __ieee754_lgamma_r(x, signgamp)
		26  // Reentrant version of the logarithm of the Gamma function
		27  // with user provided pointer for the sign of Gamma(x).
		28  //
		29  // Method:
		30  //	 1. Argument Reduction for 0 < x <= 8
		31  //			Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
		32  //			reduce x to a number in [1.5,2.5] by
		33  //							lgamma(1+s) = log(s) + lgamma(s)
		34  //			for example,
		35  //							lgamma(7.3) = log(6.3) + lgamma(6.3)
		36  //													= log(6.3*5.3) + lgamma(5.3)
		37  //													= log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
		38  //	 2. Polynomial approximation of lgamma around its
		39  //			minimum (ymin=1.461632144968362245) to maintain monotonicity.
		40  //			On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
		41  //							Let z = x-ymin;
		42  //							lgamma(x) = -1.214862905358496078218 + z**2*poly(z)
		43  //							poly(z) is a 14 degree polynomial.
		44  //	 2. Rational approximation in the primary interval [2,3]
		45  //			We use the following approximation:
		46  //							s = x-2.0;
		47  //							lgamma(x) = 0.5*s + s*P(s)/Q(s)
		48  //			with accuracy
		49  //							|P/Q - (lgamma(x)-0.5s)| < 2**-61.71
		50  //			Our algorithms are based on the following observation
		51  //
		52  //														 zeta(2)-1		2		zeta(3)-1		3
		53  // lgamma(2+s) = s*(1-Euler) + --------- * s	-	--------- * s	+ ...
		54  //																 2								 3
		55  //
		56  //			where Euler = 0.5772156649... is the Euler constant, which
		57  //			is very close to 0.5.
		58  //
		59  //	 3. For x>=8, we have
		60  //			lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
		61  //			(better formula:
		62  //				 lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
		63  //			Let z = 1/x, then we approximation
		64  //							f(z) = lgamma(x) - (x-0.5)(log(x)-1)
		65  //			by
		66  //																	3			 5						 11
		67  //							w = w0 + w1*z + w2*z	+ w3*z	+ ... + w6*z
		68  //			where
		69  //							|w - f(z)| < 2**-58.74
		70  //
		71  //	 4. For negative x, since (G is gamma function)
		72  //							-x*G(-x)*G(x) = pi/sin(pi*x),
		73  //			we have
		74  //							G(x) = pi/(sin(pi*x)*(-x)*G(-x))
		75  //			since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
		76  //			Hence, for x<0, signgam = sign(sin(pi*x)) and
		77  //							lgamma(x) = log(|Gamma(x)|)
		78  //												= log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
		79  //			Note: one should avoid computing pi*(-x) directly in the
		80  //						computation of sin(pi*(-x)).
		81  //
		82  //	 5. Special Cases
		83  //							lgamma(2+s) ~ s*(1-Euler) for tiny s
		84  //							lgamma(1)=lgamma(2)=0
		85  //							lgamma(x) ~ -log(x) for tiny x
		86  //							lgamma(0) = lgamma(inf) = inf
		87  //							lgamma(-integer) = +-inf
		88  //
		89  //
		90  
		91  var _lgamA = [...]float64{
		92  	7.72156649015328655494e-02, // 0x3FB3C467E37DB0C8
		93  	3.22467033424113591611e-01, // 0x3FD4A34CC4A60FAD
		94  	6.73523010531292681824e-02, // 0x3FB13E001A5562A7
		95  	2.05808084325167332806e-02, // 0x3F951322AC92547B
		96  	7.38555086081402883957e-03, // 0x3F7E404FB68FEFE8
		97  	2.89051383673415629091e-03, // 0x3F67ADD8CCB7926B
		98  	1.19270763183362067845e-03, // 0x3F538A94116F3F5D
		99  	5.10069792153511336608e-04, // 0x3F40B6C689B99C00
	 100  	2.20862790713908385557e-04, // 0x3F2CF2ECED10E54D
	 101  	1.08011567247583939954e-04, // 0x3F1C5088987DFB07
	 102  	2.52144565451257326939e-05, // 0x3EFA7074428CFA52
	 103  	4.48640949618915160150e-05, // 0x3F07858E90A45837
	 104  }
	 105  var _lgamR = [...]float64{
	 106  	1.0,												// placeholder
	 107  	1.39200533467621045958e+00, // 0x3FF645A762C4AB74
	 108  	7.21935547567138069525e-01, // 0x3FE71A1893D3DCDC
	 109  	1.71933865632803078993e-01, // 0x3FC601EDCCFBDF27
	 110  	1.86459191715652901344e-02, // 0x3F9317EA742ED475
	 111  	7.77942496381893596434e-04, // 0x3F497DDACA41A95B
	 112  	7.32668430744625636189e-06, // 0x3EDEBAF7A5B38140
	 113  }
	 114  var _lgamS = [...]float64{
	 115  	-7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8
	 116  	2.14982415960608852501e-01,	// 0x3FCB848B36E20878
	 117  	3.25778796408930981787e-01,	// 0x3FD4D98F4F139F59
	 118  	1.46350472652464452805e-01,	// 0x3FC2BB9CBEE5F2F7
	 119  	2.66422703033638609560e-02,	// 0x3F9B481C7E939961
	 120  	1.84028451407337715652e-03,	// 0x3F5E26B67368F239
	 121  	3.19475326584100867617e-05,	// 0x3F00BFECDD17E945
	 122  }
	 123  var _lgamT = [...]float64{
	 124  	4.83836122723810047042e-01,	// 0x3FDEF72BC8EE38A2
	 125  	-1.47587722994593911752e-01, // 0xBFC2E4278DC6C509
	 126  	6.46249402391333854778e-02,	// 0x3FB08B4294D5419B
	 127  	-3.27885410759859649565e-02, // 0xBFA0C9A8DF35B713
	 128  	1.79706750811820387126e-02,	// 0x3F9266E7970AF9EC
	 129  	-1.03142241298341437450e-02, // 0xBF851F9FBA91EC6A
	 130  	6.10053870246291332635e-03,	// 0x3F78FCE0E370E344
	 131  	-3.68452016781138256760e-03, // 0xBF6E2EFFB3E914D7
	 132  	2.25964780900612472250e-03,	// 0x3F6282D32E15C915
	 133  	-1.40346469989232843813e-03, // 0xBF56FE8EBF2D1AF1
	 134  	8.81081882437654011382e-04,	// 0x3F4CDF0CEF61A8E9
	 135  	-5.38595305356740546715e-04, // 0xBF41A6109C73E0EC
	 136  	3.15632070903625950361e-04,	// 0x3F34AF6D6C0EBBF7
	 137  	-3.12754168375120860518e-04, // 0xBF347F24ECC38C38
	 138  	3.35529192635519073543e-04,	// 0x3F35FD3EE8C2D3F4
	 139  }
	 140  var _lgamU = [...]float64{
	 141  	-7.72156649015328655494e-02, // 0xBFB3C467E37DB0C8
	 142  	6.32827064025093366517e-01,	// 0x3FE4401E8B005DFF
	 143  	1.45492250137234768737e+00,	// 0x3FF7475CD119BD6F
	 144  	9.77717527963372745603e-01,	// 0x3FEF497644EA8450
	 145  	2.28963728064692451092e-01,	// 0x3FCD4EAEF6010924
	 146  	1.33810918536787660377e-02,	// 0x3F8B678BBF2BAB09
	 147  }
	 148  var _lgamV = [...]float64{
	 149  	1.0,
	 150  	2.45597793713041134822e+00, // 0x4003A5D7C2BD619C
	 151  	2.12848976379893395361e+00, // 0x40010725A42B18F5
	 152  	7.69285150456672783825e-01, // 0x3FE89DFBE45050AF
	 153  	1.04222645593369134254e-01, // 0x3FBAAE55D6537C88
	 154  	3.21709242282423911810e-03, // 0x3F6A5ABB57D0CF61
	 155  }
	 156  var _lgamW = [...]float64{
	 157  	4.18938533204672725052e-01,	// 0x3FDACFE390C97D69
	 158  	8.33333333333329678849e-02,	// 0x3FB555555555553B
	 159  	-2.77777777728775536470e-03, // 0xBF66C16C16B02E5C
	 160  	7.93650558643019558500e-04,	// 0x3F4A019F98CF38B6
	 161  	-5.95187557450339963135e-04, // 0xBF4380CB8C0FE741
	 162  	8.36339918996282139126e-04,	// 0x3F4B67BA4CDAD5D1
	 163  	-1.63092934096575273989e-03, // 0xBF5AB89D0B9E43E4
	 164  }
	 165  
	 166  // Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x).
	 167  //
	 168  // Special cases are:
	 169  //	Lgamma(+Inf) = +Inf
	 170  //	Lgamma(0) = +Inf
	 171  //	Lgamma(-integer) = +Inf
	 172  //	Lgamma(-Inf) = -Inf
	 173  //	Lgamma(NaN) = NaN
	 174  func Lgamma(x float64) (lgamma float64, sign int) {
	 175  	const (
	 176  		Ymin	= 1.461632144968362245
	 177  		Two52 = 1 << 52										 // 0x4330000000000000 ~4.5036e+15
	 178  		Two53 = 1 << 53										 // 0x4340000000000000 ~9.0072e+15
	 179  		Two58 = 1 << 58										 // 0x4390000000000000 ~2.8823e+17
	 180  		Tiny	= 1.0 / (1 << 70)						 // 0x3b90000000000000 ~8.47033e-22
	 181  		Tc		= 1.46163214496836224576e+00	// 0x3FF762D86356BE3F
	 182  		Tf		= -1.21486290535849611461e-01 // 0xBFBF19B9BCC38A42
	 183  		// Tt = -(tail of Tf)
	 184  		Tt = -3.63867699703950536541e-18 // 0xBC50C7CAA48A971F
	 185  	)
	 186  	// special cases
	 187  	sign = 1
	 188  	switch {
	 189  	case IsNaN(x):
	 190  		lgamma = x
	 191  		return
	 192  	case IsInf(x, 0):
	 193  		lgamma = x
	 194  		return
	 195  	case x == 0:
	 196  		lgamma = Inf(1)
	 197  		return
	 198  	}
	 199  
	 200  	neg := false
	 201  	if x < 0 {
	 202  		x = -x
	 203  		neg = true
	 204  	}
	 205  
	 206  	if x < Tiny { // if |x| < 2**-70, return -log(|x|)
	 207  		if neg {
	 208  			sign = -1
	 209  		}
	 210  		lgamma = -Log(x)
	 211  		return
	 212  	}
	 213  	var nadj float64
	 214  	if neg {
	 215  		if x >= Two52 { // |x| >= 2**52, must be -integer
	 216  			lgamma = Inf(1)
	 217  			return
	 218  		}
	 219  		t := sinPi(x)
	 220  		if t == 0 {
	 221  			lgamma = Inf(1) // -integer
	 222  			return
	 223  		}
	 224  		nadj = Log(Pi / Abs(t*x))
	 225  		if t < 0 {
	 226  			sign = -1
	 227  		}
	 228  	}
	 229  
	 230  	switch {
	 231  	case x == 1 || x == 2: // purge off 1 and 2
	 232  		lgamma = 0
	 233  		return
	 234  	case x < 2: // use lgamma(x) = lgamma(x+1) - log(x)
	 235  		var y float64
	 236  		var i int
	 237  		if x <= 0.9 {
	 238  			lgamma = -Log(x)
	 239  			switch {
	 240  			case x >= (Ymin - 1 + 0.27): // 0.7316 <= x <=	0.9
	 241  				y = 1 - x
	 242  				i = 0
	 243  			case x >= (Ymin - 1 - 0.27): // 0.2316 <= x < 0.7316
	 244  				y = x - (Tc - 1)
	 245  				i = 1
	 246  			default: // 0 < x < 0.2316
	 247  				y = x
	 248  				i = 2
	 249  			}
	 250  		} else {
	 251  			lgamma = 0
	 252  			switch {
	 253  			case x >= (Ymin + 0.27): // 1.7316 <= x < 2
	 254  				y = 2 - x
	 255  				i = 0
	 256  			case x >= (Ymin - 0.27): // 1.2316 <= x < 1.7316
	 257  				y = x - Tc
	 258  				i = 1
	 259  			default: // 0.9 < x < 1.2316
	 260  				y = x - 1
	 261  				i = 2
	 262  			}
	 263  		}
	 264  		switch i {
	 265  		case 0:
	 266  			z := y * y
	 267  			p1 := _lgamA[0] + z*(_lgamA[2]+z*(_lgamA[4]+z*(_lgamA[6]+z*(_lgamA[8]+z*_lgamA[10]))))
	 268  			p2 := z * (_lgamA[1] + z*(+_lgamA[3]+z*(_lgamA[5]+z*(_lgamA[7]+z*(_lgamA[9]+z*_lgamA[11])))))
	 269  			p := y*p1 + p2
	 270  			lgamma += (p - 0.5*y)
	 271  		case 1:
	 272  			z := y * y
	 273  			w := z * y
	 274  			p1 := _lgamT[0] + w*(_lgamT[3]+w*(_lgamT[6]+w*(_lgamT[9]+w*_lgamT[12]))) // parallel comp
	 275  			p2 := _lgamT[1] + w*(_lgamT[4]+w*(_lgamT[7]+w*(_lgamT[10]+w*_lgamT[13])))
	 276  			p3 := _lgamT[2] + w*(_lgamT[5]+w*(_lgamT[8]+w*(_lgamT[11]+w*_lgamT[14])))
	 277  			p := z*p1 - (Tt - w*(p2+y*p3))
	 278  			lgamma += (Tf + p)
	 279  		case 2:
	 280  			p1 := y * (_lgamU[0] + y*(_lgamU[1]+y*(_lgamU[2]+y*(_lgamU[3]+y*(_lgamU[4]+y*_lgamU[5])))))
	 281  			p2 := 1 + y*(_lgamV[1]+y*(_lgamV[2]+y*(_lgamV[3]+y*(_lgamV[4]+y*_lgamV[5]))))
	 282  			lgamma += (-0.5*y + p1/p2)
	 283  		}
	 284  	case x < 8: // 2 <= x < 8
	 285  		i := int(x)
	 286  		y := x - float64(i)
	 287  		p := y * (_lgamS[0] + y*(_lgamS[1]+y*(_lgamS[2]+y*(_lgamS[3]+y*(_lgamS[4]+y*(_lgamS[5]+y*_lgamS[6]))))))
	 288  		q := 1 + y*(_lgamR[1]+y*(_lgamR[2]+y*(_lgamR[3]+y*(_lgamR[4]+y*(_lgamR[5]+y*_lgamR[6])))))
	 289  		lgamma = 0.5*y + p/q
	 290  		z := 1.0 // Lgamma(1+s) = Log(s) + Lgamma(s)
	 291  		switch i {
	 292  		case 7:
	 293  			z *= (y + 6)
	 294  			fallthrough
	 295  		case 6:
	 296  			z *= (y + 5)
	 297  			fallthrough
	 298  		case 5:
	 299  			z *= (y + 4)
	 300  			fallthrough
	 301  		case 4:
	 302  			z *= (y + 3)
	 303  			fallthrough
	 304  		case 3:
	 305  			z *= (y + 2)
	 306  			lgamma += Log(z)
	 307  		}
	 308  	case x < Two58: // 8 <= x < 2**58
	 309  		t := Log(x)
	 310  		z := 1 / x
	 311  		y := z * z
	 312  		w := _lgamW[0] + z*(_lgamW[1]+y*(_lgamW[2]+y*(_lgamW[3]+y*(_lgamW[4]+y*(_lgamW[5]+y*_lgamW[6])))))
	 313  		lgamma = (x-0.5)*(t-1) + w
	 314  	default: // 2**58 <= x <= Inf
	 315  		lgamma = x * (Log(x) - 1)
	 316  	}
	 317  	if neg {
	 318  		lgamma = nadj - lgamma
	 319  	}
	 320  	return
	 321  }
	 322  
	 323  // sinPi(x) is a helper function for negative x
	 324  func sinPi(x float64) float64 {
	 325  	const (
	 326  		Two52 = 1 << 52 // 0x4330000000000000 ~4.5036e+15
	 327  		Two53 = 1 << 53 // 0x4340000000000000 ~9.0072e+15
	 328  	)
	 329  	if x < 0.25 {
	 330  		return -Sin(Pi * x)
	 331  	}
	 332  
	 333  	// argument reduction
	 334  	z := Floor(x)
	 335  	var n int
	 336  	if z != x { // inexact
	 337  		x = Mod(x, 2)
	 338  		n = int(x * 4)
	 339  	} else {
	 340  		if x >= Two53 { // x must be even
	 341  			x = 0
	 342  			n = 0
	 343  		} else {
	 344  			if x < Two52 {
	 345  				z = x + Two52 // exact
	 346  			}
	 347  			n = int(1 & Float64bits(z))
	 348  			x = float64(n)
	 349  			n <<= 2
	 350  		}
	 351  	}
	 352  	switch n {
	 353  	case 0:
	 354  		x = Sin(Pi * x)
	 355  	case 1, 2:
	 356  		x = Cos(Pi * (0.5 - x))
	 357  	case 3, 4:
	 358  		x = Sin(Pi * (1 - x))
	 359  	case 5, 6:
	 360  		x = -Cos(Pi * (x - 1.5))
	 361  	default:
	 362  		x = Sin(Pi * (x - 2))
	 363  	}
	 364  	return -x
	 365  }
	 366  

View as plain text