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 }