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 }