gonum.org/v1/gonum@v0.14.0/mathext/internal/cephes/unity.go (about)

     1  // Derived from SciPy's special/cephes/unity.c
     2  // https://github.com/scipy/scipy/blob/master/scipy/special/cephes/unity.c
     3  // Made freely available by Stephen L. Moshier without support or guarantee.
     4  
     5  // Use of this source code is governed by a BSD-style
     6  // license that can be found in the LICENSE file.
     7  // Copyright ©1984, ©1996 by Stephen L. Moshier
     8  // Portions Copyright ©2016 The Gonum Authors. All rights reserved.
     9  
    10  package cephes
    11  
    12  import "math"
    13  
    14  // Relative error approximations for function arguments near unity.
    15  //  log1p(x) = log(1+x)
    16  //  expm1(x) = exp(x) - 1
    17  //  cosm1(x) = cos(x) - 1
    18  //  lgam1p(x) = lgam(1+x)
    19  
    20  const (
    21  	invSqrt2 = 1 / math.Sqrt2
    22  	pi4      = math.Pi / 4
    23  	euler    = 0.577215664901532860606512090082402431 // Euler constant
    24  )
    25  
    26  // Coefficients for
    27  //
    28  //	log(1+x) = x - \frac{x^2}{2} + \frac{x^3 lP(x)}{lQ(x)}
    29  //
    30  // for
    31  //
    32  //	\frac{1}{\sqrt{2}} <= x < \sqrt{2}
    33  //
    34  // Theoretical peak relative error = 2.32e-20
    35  var lP = [...]float64{
    36  	4.5270000862445199635215e-5,
    37  	4.9854102823193375972212e-1,
    38  	6.5787325942061044846969e0,
    39  	2.9911919328553073277375e1,
    40  	6.0949667980987787057556e1,
    41  	5.7112963590585538103336e1,
    42  	2.0039553499201281259648e1,
    43  }
    44  
    45  var lQ = [...]float64{
    46  	1.5062909083469192043167e1,
    47  	8.3047565967967209469434e1,
    48  	2.2176239823732856465394e2,
    49  	3.0909872225312059774938e2,
    50  	2.1642788614495947685003e2,
    51  	6.0118660497603843919306e1,
    52  }
    53  
    54  // log1p computes
    55  //
    56  //	log(1 + x)
    57  func log1p(x float64) float64 {
    58  	z := 1 + x
    59  	if z < invSqrt2 || z > math.Sqrt2 {
    60  		return math.Log(z)
    61  	}
    62  	z = x * x
    63  	z = -0.5*z + x*(z*polevl(x, lP[:], 6)/p1evl(x, lQ[:], 6))
    64  	return x + z
    65  }
    66  
    67  // log1pmx computes
    68  //
    69  //	log(1 + x) - x
    70  func log1pmx(x float64) float64 {
    71  	if math.Abs(x) < 0.5 {
    72  		xfac := x
    73  		res := 0.0
    74  
    75  		var term float64
    76  		for n := 2; n < maxIter; n++ {
    77  			xfac *= -x
    78  			term = xfac / float64(n)
    79  			res += term
    80  			if math.Abs(term) < machEp*math.Abs(res) {
    81  				break
    82  			}
    83  		}
    84  		return res
    85  	}
    86  	return log1p(x) - x
    87  }
    88  
    89  // Coefficients for
    90  //
    91  //	e^x = 1 + \frac{2x eP(x^2)}{eQ(x^2) - eP(x^2)}
    92  //
    93  // for
    94  //
    95  //	-0.5 <= x <= 0.5
    96  var eP = [...]float64{
    97  	1.2617719307481059087798e-4,
    98  	3.0299440770744196129956e-2,
    99  	9.9999999999999999991025e-1,
   100  }
   101  
   102  var eQ = [...]float64{
   103  	3.0019850513866445504159e-6,
   104  	2.5244834034968410419224e-3,
   105  	2.2726554820815502876593e-1,
   106  	2.0000000000000000000897e0,
   107  }
   108  
   109  // expm1 computes
   110  //
   111  //	expm1(x) = e^x - 1
   112  func expm1(x float64) float64 {
   113  	if math.IsInf(x, 0) {
   114  		if math.IsNaN(x) || x > 0 {
   115  			return x
   116  		}
   117  		return -1
   118  	}
   119  	if x < -0.5 || x > 0.5 {
   120  		return math.Exp(x) - 1
   121  	}
   122  	xx := x * x
   123  	r := x * polevl(xx, eP[:], 2)
   124  	r = r / (polevl(xx, eQ[:], 3) - r)
   125  	return r + r
   126  }
   127  
   128  var coscof = [...]float64{
   129  	4.7377507964246204691685e-14,
   130  	-1.1470284843425359765671e-11,
   131  	2.0876754287081521758361e-9,
   132  	-2.7557319214999787979814e-7,
   133  	2.4801587301570552304991e-5,
   134  	-1.3888888888888872993737e-3,
   135  	4.1666666666666666609054e-2,
   136  }
   137  
   138  // cosm1 computes
   139  //
   140  //	cosm1(x) = cos(x) - 1
   141  func cosm1(x float64) float64 {
   142  	if x < -pi4 || x > pi4 {
   143  		return math.Cos(x) - 1
   144  	}
   145  	xx := x * x
   146  	xx = -0.5*xx + xx*xx*polevl(xx, coscof[:], 6)
   147  	return xx
   148  }
   149  
   150  // lgam1pTayler computes
   151  //
   152  //	lgam(x + 1)
   153  //
   154  // around x = 0 using its Taylor series.
   155  func lgam1pTaylor(x float64) float64 {
   156  	if x == 0 {
   157  		return 0
   158  	}
   159  	res := -euler * x
   160  	xfac := -x
   161  	for n := 2; n < 42; n++ {
   162  		nf := float64(n)
   163  		xfac *= -x
   164  		coeff := Zeta(nf, 1) * xfac / nf
   165  		res += coeff
   166  		if math.Abs(coeff) < machEp*math.Abs(res) {
   167  			break
   168  		}
   169  	}
   170  
   171  	return res
   172  }
   173  
   174  // lgam1p computes
   175  //
   176  //	lgam(x + 1)
   177  func lgam1p(x float64) float64 {
   178  	if math.Abs(x) <= 0.5 {
   179  		return lgam1pTaylor(x)
   180  	} else if math.Abs(x-1) < 0.5 {
   181  		return math.Log(x) + lgam1pTaylor(x-1)
   182  	}
   183  	return lgam(x + 1)
   184  }