// Derived from SciPy's special/cephes/unity.c // https://github.com/scipy/scipy/blob/master/scipy/special/cephes/unity.c // Made freely available by Stephen L. Moshier without support or guarantee. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Copyright ©1984, ©1996 by Stephen L. Moshier // Portions Copyright ©2016 The Gonum Authors. All rights reserved. package cephes import "math" // Relative error approximations for function arguments near unity. // log1p(x) = log(1+x) // expm1(x) = exp(x) - 1 // cosm1(x) = cos(x) - 1 // lgam1p(x) = lgam(1+x) const ( invSqrt2 = 1 / math.Sqrt2 pi4 = math.Pi / 4 euler = 0.577215664901532860606512090082402431 // Euler constant ) // Coefficients for // log(1+x) = x - \frac{x^2}{2} + \frac{x^3 lP(x)}{lQ(x)} // for // \frac{1}{\sqrt{2}} <= x < \sqrt{2} // Theoretical peak relative error = 2.32e-20 var lP = [...]float64{ 4.5270000862445199635215E-5, 4.9854102823193375972212E-1, 6.5787325942061044846969E0, 2.9911919328553073277375E1, 6.0949667980987787057556E1, 5.7112963590585538103336E1, 2.0039553499201281259648E1, } var lQ = [...]float64{ 1.5062909083469192043167E1, 8.3047565967967209469434E1, 2.2176239823732856465394E2, 3.0909872225312059774938E2, 2.1642788614495947685003E2, 6.0118660497603843919306E1, } // log1p computes // log(1 + x) func log1p(x float64) float64 { z := 1 + x if z < invSqrt2 || z > math.Sqrt2 { return math.Log(z) } z = x * x z = -0.5*z + x*(z*polevl(x, lP[:], 6)/p1evl(x, lQ[:], 6)) return x + z } // log1pmx computes // log(1 + x) - x func log1pmx(x float64) float64 { if math.Abs(x) < 0.5 { xfac := x res := 0.0 var term float64 for n := 2; n < maxIter; n++ { xfac *= -x term = xfac / float64(n) res += term if math.Abs(term) < machEp*math.Abs(res) { break } } return res } return log1p(x) - x } // Coefficients for // e^x = 1 + \frac{2x eP(x^2)}{eQ(x^2) - eP(x^2)} // for // -0.5 <= x <= 0.5 var eP = [...]float64{ 1.2617719307481059087798E-4, 3.0299440770744196129956E-2, 9.9999999999999999991025E-1, } var eQ = [...]float64{ 3.0019850513866445504159E-6, 2.5244834034968410419224E-3, 2.2726554820815502876593E-1, 2.0000000000000000000897E0, } // expm1 computes // expm1(x) = e^x - 1 func expm1(x float64) float64 { if math.IsInf(x, 0) { if math.IsNaN(x) || x > 0 { return x } return -1 } if x < -0.5 || x > 0.5 { return math.Exp(x) - 1 } xx := x * x r := x * polevl(xx, eP[:], 2) r = r / (polevl(xx, eQ[:], 3) - r) return r + r } var coscof = [...]float64{ 4.7377507964246204691685E-14, -1.1470284843425359765671E-11, 2.0876754287081521758361E-9, -2.7557319214999787979814E-7, 2.4801587301570552304991E-5, -1.3888888888888872993737E-3, 4.1666666666666666609054E-2, } // cosm1 computes // cosm1(x) = cos(x) - 1 func cosm1(x float64) float64 { if x < -pi4 || x > pi4 { return math.Cos(x) - 1 } xx := x * x xx = -0.5*xx + xx*xx*polevl(xx, coscof[:], 6) return xx } // lgam1pTayler computes // lgam(x + 1) //around x = 0 using its Taylor series. func lgam1pTaylor(x float64) float64 { if x == 0 { return 0 } res := -euler * x xfac := -x for n := 2; n < 42; n++ { nf := float64(n) xfac *= -x coeff := Zeta(nf, 1) * xfac / nf res += coeff if math.Abs(coeff) < machEp*math.Abs(res) { break } } return res } // lgam1p computes // lgam(x + 1) func lgam1p(x float64) float64 { if math.Abs(x) <= 0.5 { return lgam1pTaylor(x) } else if math.Abs(x-1) < 0.5 { return math.Log(x) + lgam1pTaylor(x-1) } return lgam(x + 1) }