github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/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  //  log(1+x) = x - \frac{x^2}{2} + \frac{x^3 lP(x)}{lQ(x)}
    28  // for
    29  //  \frac{1}{\sqrt{2}} <= x < \sqrt{2}
    30  // Theoretical peak relative error = 2.32e-20
    31  var lP = [...]float64{
    32  	4.5270000862445199635215e-5,
    33  	4.9854102823193375972212e-1,
    34  	6.5787325942061044846969e0,
    35  	2.9911919328553073277375e1,
    36  	6.0949667980987787057556e1,
    37  	5.7112963590585538103336e1,
    38  	2.0039553499201281259648e1,
    39  }
    40  
    41  var lQ = [...]float64{
    42  	1.5062909083469192043167e1,
    43  	8.3047565967967209469434e1,
    44  	2.2176239823732856465394e2,
    45  	3.0909872225312059774938e2,
    46  	2.1642788614495947685003e2,
    47  	6.0118660497603843919306e1,
    48  }
    49  
    50  // log1p computes
    51  //  log(1 + x)
    52  func log1p(x float64) float64 {
    53  	z := 1 + x
    54  	if z < invSqrt2 || z > math.Sqrt2 {
    55  		return math.Log(z)
    56  	}
    57  	z = x * x
    58  	z = -0.5*z + x*(z*polevl(x, lP[:], 6)/p1evl(x, lQ[:], 6))
    59  	return x + z
    60  }
    61  
    62  // log1pmx computes
    63  //  log(1 + x) - x
    64  func log1pmx(x float64) float64 {
    65  	if math.Abs(x) < 0.5 {
    66  		xfac := x
    67  		res := 0.0
    68  
    69  		var term float64
    70  		for n := 2; n < maxIter; n++ {
    71  			xfac *= -x
    72  			term = xfac / float64(n)
    73  			res += term
    74  			if math.Abs(term) < machEp*math.Abs(res) {
    75  				break
    76  			}
    77  		}
    78  		return res
    79  	}
    80  	return log1p(x) - x
    81  }
    82  
    83  // Coefficients for
    84  //  e^x = 1 + \frac{2x eP(x^2)}{eQ(x^2) - eP(x^2)}
    85  // for
    86  //  -0.5 <= x <= 0.5
    87  var eP = [...]float64{
    88  	1.2617719307481059087798e-4,
    89  	3.0299440770744196129956e-2,
    90  	9.9999999999999999991025e-1,
    91  }
    92  
    93  var eQ = [...]float64{
    94  	3.0019850513866445504159e-6,
    95  	2.5244834034968410419224e-3,
    96  	2.2726554820815502876593e-1,
    97  	2.0000000000000000000897e0,
    98  }
    99  
   100  // expm1 computes
   101  //  expm1(x) = e^x - 1
   102  func expm1(x float64) float64 {
   103  	if math.IsInf(x, 0) {
   104  		if math.IsNaN(x) || x > 0 {
   105  			return x
   106  		}
   107  		return -1
   108  	}
   109  	if x < -0.5 || x > 0.5 {
   110  		return math.Exp(x) - 1
   111  	}
   112  	xx := x * x
   113  	r := x * polevl(xx, eP[:], 2)
   114  	r = r / (polevl(xx, eQ[:], 3) - r)
   115  	return r + r
   116  }
   117  
   118  var coscof = [...]float64{
   119  	4.7377507964246204691685e-14,
   120  	-1.1470284843425359765671e-11,
   121  	2.0876754287081521758361e-9,
   122  	-2.7557319214999787979814e-7,
   123  	2.4801587301570552304991e-5,
   124  	-1.3888888888888872993737e-3,
   125  	4.1666666666666666609054e-2,
   126  }
   127  
   128  // cosm1 computes
   129  //  cosm1(x) = cos(x) - 1
   130  func cosm1(x float64) float64 {
   131  	if x < -pi4 || x > pi4 {
   132  		return math.Cos(x) - 1
   133  	}
   134  	xx := x * x
   135  	xx = -0.5*xx + xx*xx*polevl(xx, coscof[:], 6)
   136  	return xx
   137  }
   138  
   139  // lgam1pTayler computes
   140  //  lgam(x + 1)
   141  //around x = 0 using its Taylor series.
   142  func lgam1pTaylor(x float64) float64 {
   143  	if x == 0 {
   144  		return 0
   145  	}
   146  	res := -euler * x
   147  	xfac := -x
   148  	for n := 2; n < 42; n++ {
   149  		nf := float64(n)
   150  		xfac *= -x
   151  		coeff := Zeta(nf, 1) * xfac / nf
   152  		res += coeff
   153  		if math.Abs(coeff) < machEp*math.Abs(res) {
   154  			break
   155  		}
   156  	}
   157  
   158  	return res
   159  }
   160  
   161  // lgam1p computes
   162  //  lgam(x + 1)
   163  func lgam1p(x float64) float64 {
   164  	if math.Abs(x) <= 0.5 {
   165  		return lgam1pTaylor(x)
   166  	} else if math.Abs(x-1) < 0.5 {
   167  		return math.Log(x) + lgam1pTaylor(x-1)
   168  	}
   169  	return lgam(x + 1)
   170  }