mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-25 01:50:30 -04:00 
			
		
		
		
	
		
			
	
	
		
			162 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			162 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | [section:lgamma Log Gamma] | ||
|  | 
 | ||
|  | [h4 Synopsis] | ||
|  | 
 | ||
|  | `` | ||
|  | #include <boost/math/special_functions/gamma.hpp> | ||
|  | `` | ||
|  | 
 | ||
|  |    namespace boost{ namespace math{ | ||
|  |     | ||
|  |    template <class T> | ||
|  |    ``__sf_result`` lgamma(T z); | ||
|  |     | ||
|  |    template <class T, class ``__Policy``> | ||
|  |    ``__sf_result`` lgamma(T z, const ``__Policy``&); | ||
|  |     | ||
|  |    template <class T> | ||
|  |    ``__sf_result`` lgamma(T z, int* sign); | ||
|  |     | ||
|  |    template <class T, class ``__Policy``> | ||
|  |    ``__sf_result`` lgamma(T z, int* sign, const ``__Policy``&); | ||
|  |     | ||
|  |    }} // namespaces | ||
|  | 
 | ||
|  | [h4 Description] | ||
|  | 
 | ||
|  | The [@http://en.wikipedia.org/wiki/Gamma_function lgamma function] is defined by: | ||
|  | 
 | ||
|  | [equation lgamm1] | ||
|  | 
 | ||
|  | The second form of the function takes a pointer to an integer, | ||
|  | which if non-null is set on output to the sign of tgamma(z). | ||
|  | 
 | ||
|  | [optional_policy] | ||
|  | 
 | ||
|  | [graph lgamma] | ||
|  | 
 | ||
|  | There are effectively two versions of this function internally: a fully | ||
|  | generic version that is slow, but reasonably accurate, and a much more | ||
|  | efficient approximation that is used where the number of digits in the significand | ||
|  | of T correspond to a certain __lanczos.  In practice, any built-in | ||
|  | floating-point type you will encounter has an appropriate __lanczos | ||
|  | defined for it.  It is also possible, given enough machine time, to generate | ||
|  | further __lanczos's using the program libs/math/tools/lanczos_generator.cpp. | ||
|  | 
 | ||
|  | The return type of these functions is computed using the __arg_promotion_rules: | ||
|  | the result is of type `double` if T is an integer type, or type T otherwise. | ||
|  | 
 | ||
|  | [h4 Accuracy] | ||
|  | 
 | ||
|  | The following table shows the peak errors (in units of epsilon)  | ||
|  | found on various platforms | ||
|  | with various floating point types, along with comparisons to  | ||
|  | various other libraries. Unless otherwise specified any | ||
|  | floating point type that is narrower than the one shown will have | ||
|  | __zero_error. | ||
|  | 
 | ||
|  | Note that while the relative errors near the positive roots of lgamma | ||
|  | are very low, the lgamma function has an infinite number of irrational | ||
|  | roots for negative arguments: very close to these negative roots only | ||
|  | a low absolute error can be guaranteed. | ||
|  | 
 | ||
|  | [table_lgamma] | ||
|  | 
 | ||
|  | [h4 Testing] | ||
|  | 
 | ||
|  | The main tests for this function involve comparisons against the logs of  | ||
|  | the factorials which can be independently calculated to very high accuracy. | ||
|  | 
 | ||
|  | Random tests in key problem areas are also used. | ||
|  | 
 | ||
|  | [h4 Implementation] | ||
|  | 
 | ||
|  | The generic version of this function is implemented using Sterling's approximation | ||
|  | for large arguments: | ||
|  | 
 | ||
|  | [equation gamma6] | ||
|  | 
 | ||
|  | For small arguments, the logarithm of tgamma is used. | ||
|  | 
 | ||
|  | For negative /z/ the logarithm version of the  | ||
|  | reflection formula is used: | ||
|  | 
 | ||
|  | [equation lgamm3] | ||
|  | 
 | ||
|  | For types of known precision, the __lanczos is used, a traits class  | ||
|  | `boost::math::lanczos::lanczos_traits` maps type T to an appropriate | ||
|  | approximation.  The logarithmic version of the __lanczos is: | ||
|  | 
 | ||
|  | [equation lgamm4] | ||
|  | 
 | ||
|  | Where L[sub e,g][space] is the Lanczos sum, scaled by e[super g]. | ||
|  | 
 | ||
|  | As before the reflection formula is used for /z < 0/. | ||
|  | 
 | ||
|  | When z is very near 1 or 2, then the logarithmic version of the __lanczos | ||
|  | suffers very badly from cancellation error: indeed for values sufficiently | ||
|  | close to 1 or 2, arbitrarily large relative errors can be obtained (even though | ||
|  | the absolute error is tiny).   | ||
|  | 
 | ||
|  | For types with up to 113 bits of precision | ||
|  | (up to and including 128-bit long doubles), root-preserving  | ||
|  | rational approximations [jm_rationals] are used | ||
|  | over the intervals [1,2] and [2,3].  Over the interval [2,3] the approximation | ||
|  | form used is: | ||
|  | 
 | ||
|  |    lgamma(z) = (z-2)(z+1)(Y + R(z-2)); | ||
|  |     | ||
|  | Where Y is a constant, and R(z-2) is the rational approximation: optimised | ||
|  | so that it's absolute error is tiny compared to Y.  In addition  | ||
|  | small values of z greater | ||
|  | than 3 can handled by argument reduction using the recurrence relation: | ||
|  | 
 | ||
|  |    lgamma(z+1) = log(z) + lgamma(z); | ||
|  |     | ||
|  | Over the interval [1,2] two approximations have to be used, one for small z uses: | ||
|  | 
 | ||
|  |    lgamma(z) = (z-1)(z-2)(Y + R(z-1)); | ||
|  |     | ||
|  | Once again Y is a constant, and R(z-1) is optimised for low absolute error | ||
|  | compared to Y.  For z > 1.5 the above form wouldn't converge to a  | ||
|  | minimax solution but this similar form does: | ||
|  | 
 | ||
|  |    lgamma(z) = (2-z)(1-z)(Y + R(2-z)); | ||
|  |     | ||
|  | Finally for z < 1 the recurrence relation can be used to move to z > 1: | ||
|  | 
 | ||
|  |    lgamma(z) = lgamma(z+1) - log(z); | ||
|  |     | ||
|  | Note that while this involves a subtraction, it appears not | ||
|  | to suffer from cancellation error: as z decreases from 1  | ||
|  | the `-log(z)` term grows positive much more | ||
|  | rapidly than the `lgamma(z+1)` term becomes negative.  So in this | ||
|  | specific case, significant digits are preserved, rather than cancelled. | ||
|  | 
 | ||
|  | For other types which do have a __lanczos defined for them  | ||
|  | the current solution is as follows: imagine we | ||
|  | balance the two terms in the __lanczos by dividing the power term by its value | ||
|  | at /z = 1/, and then multiplying the Lanczos coefficients by the same value. | ||
|  | Now each term will take the value 1 at /z = 1/ and we can rearrange the power terms | ||
|  | in terms of log1p.  Likewise if we subtract 1 from the Lanczos sum part  | ||
|  | (algebraically, by subtracting the value of each term at /z = 1/), we obtain | ||
|  | a new summation that can be also be fed into log1p.  Crucially, all of the  | ||
|  | terms tend to zero, as /z -> 1/: | ||
|  | 
 | ||
|  | [equation lgamm5] | ||
|  | 
 | ||
|  | The C[sub k][space] terms in the above are the same as in the __lanczos. | ||
|  | 
 | ||
|  | A similar rearrangement can be performed at /z = 2/: | ||
|  | 
 | ||
|  | [equation lgamm6] | ||
|  | 
 | ||
|  | [endsect][/section:lgamma The Log Gamma Function] | ||
|  | 
 | ||
|  | [/  | ||
|  |   Copyright 2006 John Maddock and Paul A. Bristow. | ||
|  |   Distributed under the Boost Software License, Version 1.0. | ||
|  |   (See accompanying file LICENSE_1_0.txt or copy at | ||
|  |   http://www.boost.org/LICENSE_1_0.txt). | ||
|  | ] |