| // Copyright 2010 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package cmplx |
| |
| import ( |
| "math" |
| "math/bits" |
| ) |
| |
| // The original C code, the long comment, and the constants |
| // below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c. |
| // The go code is a simplified version of the original C. |
| // |
| // Cephes Math Library Release 2.8: June, 2000 |
| // Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier |
| // |
| // The readme file at http://netlib.sandia.gov/cephes/ says: |
| // Some software in this archive may be from the book _Methods and |
| // Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster |
| // International, 1989) or from the Cephes Mathematical Library, a |
| // commercial product. In either event, it is copyrighted by the author. |
| // What you see here may be used freely but it comes with no support or |
| // guarantee. |
| // |
| // The two known misprints in the book are repaired here in the |
| // source listings for the gamma function and the incomplete beta |
| // integral. |
| // |
| // Stephen L. Moshier |
| // moshier@na-net.ornl.gov |
| |
| // Complex circular tangent |
| // |
| // DESCRIPTION: |
| // |
| // If |
| // z = x + iy, |
| // |
| // then |
| // |
| // sin 2x + i sinh 2y |
| // w = --------------------. |
| // cos 2x + cosh 2y |
| // |
| // On the real axis the denominator is zero at odd multiples |
| // of PI/2. The denominator is evaluated by its Taylor |
| // series near these points. |
| // |
| // ctan(z) = -i ctanh(iz). |
| // |
| // ACCURACY: |
| // |
| // Relative error: |
| // arithmetic domain # trials peak rms |
| // DEC -10,+10 5200 7.1e-17 1.6e-17 |
| // IEEE -10,+10 30000 7.2e-16 1.2e-16 |
| // Also tested by ctan * ccot = 1 and catan(ctan(z)) = z. |
| |
| // Tan returns the tangent of x. |
| func Tan(x complex128) complex128 { |
| switch re, im := real(x), imag(x); { |
| case math.IsInf(im, 0): |
| switch { |
| case math.IsInf(re, 0) || math.IsNaN(re): |
| return complex(math.Copysign(0, re), math.Copysign(1, im)) |
| } |
| return complex(math.Copysign(0, math.Sin(2*re)), math.Copysign(1, im)) |
| case re == 0 && math.IsNaN(im): |
| return x |
| } |
| d := math.Cos(2*real(x)) + math.Cosh(2*imag(x)) |
| if math.Abs(d) < 0.25 { |
| d = tanSeries(x) |
| } |
| if d == 0 { |
| return Inf() |
| } |
| return complex(math.Sin(2*real(x))/d, math.Sinh(2*imag(x))/d) |
| } |
| |
| // Complex hyperbolic tangent |
| // |
| // DESCRIPTION: |
| // |
| // tanh z = (sinh 2x + i sin 2y) / (cosh 2x + cos 2y) . |
| // |
| // ACCURACY: |
| // |
| // Relative error: |
| // arithmetic domain # trials peak rms |
| // IEEE -10,+10 30000 1.7e-14 2.4e-16 |
| |
| // Tanh returns the hyperbolic tangent of x. |
| func Tanh(x complex128) complex128 { |
| switch re, im := real(x), imag(x); { |
| case math.IsInf(re, 0): |
| switch { |
| case math.IsInf(im, 0) || math.IsNaN(im): |
| return complex(math.Copysign(1, re), math.Copysign(0, im)) |
| } |
| return complex(math.Copysign(1, re), math.Copysign(0, math.Sin(2*im))) |
| case im == 0 && math.IsNaN(re): |
| return x |
| } |
| d := math.Cosh(2*real(x)) + math.Cos(2*imag(x)) |
| if d == 0 { |
| return Inf() |
| } |
| return complex(math.Sinh(2*real(x))/d, math.Sin(2*imag(x))/d) |
| } |
| |
| // reducePi reduces the input argument x to the range (-Pi/2, Pi/2]. |
| // x must be greater than or equal to 0. For small arguments it |
| // uses Cody-Waite reduction in 3 float64 parts based on: |
| // "Elementary Function Evaluation: Algorithms and Implementation" |
| // Jean-Michel Muller, 1997. |
| // For very large arguments it uses Payne-Hanek range reduction based on: |
| // "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit" |
| // K. C. Ng et al, March 24, 1992. |
| func reducePi(x float64) float64 { |
| // reduceThreshold is the maximum value of x where the reduction using |
| // Cody-Waite reduction still gives accurate results. This threshold |
| // is set by t*PIn being representable as a float64 without error |
| // where t is given by t = floor(x * (1 / Pi)) and PIn are the leading partial |
| // terms of Pi. Since the leading terms, PI1 and PI2 below, have 30 and 32 |
| // trailing zero bits respectively, t should have less than 30 significant bits. |
| // t < 1<<30 -> floor(x*(1/Pi)+0.5) < 1<<30 -> x < (1<<30-1) * Pi - 0.5 |
| // So, conservatively we can take x < 1<<30. |
| const reduceThreshold float64 = 1 << 30 |
| if math.Abs(x) < reduceThreshold { |
| // Use Cody-Waite reduction in three parts. |
| const ( |
| // PI1, PI2 and PI3 comprise an extended precision value of PI |
| // such that PI ~= PI1 + PI2 + PI3. The parts are chosen so |
| // that PI1 and PI2 have an approximately equal number of trailing |
| // zero bits. This ensures that t*PI1 and t*PI2 are exact for |
| // large integer values of t. The full precision PI3 ensures the |
| // approximation of PI is accurate to 102 bits to handle cancellation |
| // during subtraction. |
| PI1 = 3.141592502593994 // 0x400921fb40000000 |
| PI2 = 1.5099578831723193e-07 // 0x3e84442d00000000 |
| PI3 = 1.0780605716316238e-14 // 0x3d08469898cc5170 |
| ) |
| t := x / math.Pi |
| t += 0.5 |
| t = float64(int64(t)) // int64(t) = the multiple |
| return ((x - t*PI1) - t*PI2) - t*PI3 |
| } |
| // Must apply Payne-Hanek range reduction |
| const ( |
| mask = 0x7FF |
| shift = 64 - 11 - 1 |
| bias = 1023 |
| fracMask = 1<<shift - 1 |
| ) |
| // Extract out the integer and exponent such that, |
| // x = ix * 2 ** exp. |
| ix := math.Float64bits(x) |
| exp := int(ix>>shift&mask) - bias - shift |
| ix &= fracMask |
| ix |= 1 << shift |
| |
| // mPi is the binary digits of 1/Pi as a uint64 array, |
| // that is, 1/Pi = Sum mPi[i]*2^(-64*i). |
| // 19 64-bit digits give 1216 bits of precision |
| // to handle the largest possible float64 exponent. |
| var mPi = [...]uint64{ |
| 0x0000000000000000, |
| 0x517cc1b727220a94, |
| 0xfe13abe8fa9a6ee0, |
| 0x6db14acc9e21c820, |
| 0xff28b1d5ef5de2b0, |
| 0xdb92371d2126e970, |
| 0x0324977504e8c90e, |
| 0x7f0ef58e5894d39f, |
| 0x74411afa975da242, |
| 0x74ce38135a2fbf20, |
| 0x9cc8eb1cc1a99cfa, |
| 0x4e422fc5defc941d, |
| 0x8ffc4bffef02cc07, |
| 0xf79788c5ad05368f, |
| 0xb69b3f6793e584db, |
| 0xa7a31fb34f2ff516, |
| 0xba93dd63f5f2f8bd, |
| 0x9e839cfbc5294975, |
| 0x35fdafd88fc6ae84, |
| 0x2b0198237e3db5d5, |
| } |
| // Use the exponent to extract the 3 appropriate uint64 digits from mPi, |
| // B ~ (z0, z1, z2), such that the product leading digit has the exponent -64. |
| // Note, exp >= 50 since x >= reduceThreshold and exp < 971 for maximum float64. |
| digit, bitshift := uint(exp+64)/64, uint(exp+64)%64 |
| z0 := (mPi[digit] << bitshift) | (mPi[digit+1] >> (64 - bitshift)) |
| z1 := (mPi[digit+1] << bitshift) | (mPi[digit+2] >> (64 - bitshift)) |
| z2 := (mPi[digit+2] << bitshift) | (mPi[digit+3] >> (64 - bitshift)) |
| // Multiply mantissa by the digits and extract the upper two digits (hi, lo). |
| z2hi, _ := bits.Mul64(z2, ix) |
| z1hi, z1lo := bits.Mul64(z1, ix) |
| z0lo := z0 * ix |
| lo, c := bits.Add64(z1lo, z2hi, 0) |
| hi, _ := bits.Add64(z0lo, z1hi, c) |
| // Find the magnitude of the fraction. |
| lz := uint(bits.LeadingZeros64(hi)) |
| e := uint64(bias - (lz + 1)) |
| // Clear implicit mantissa bit and shift into place. |
| hi = (hi << (lz + 1)) | (lo >> (64 - (lz + 1))) |
| hi >>= 64 - shift |
| // Include the exponent and convert to a float. |
| hi |= e << shift |
| x = math.Float64frombits(hi) |
| // map to (-Pi/2, Pi/2] |
| if x > 0.5 { |
| x-- |
| } |
| return math.Pi * x |
| } |
| |
| // Taylor series expansion for cosh(2y) - cos(2x) |
| func tanSeries(z complex128) float64 { |
| const MACHEP = 1.0 / (1 << 53) |
| x := math.Abs(2 * real(z)) |
| y := math.Abs(2 * imag(z)) |
| x = reducePi(x) |
| x = x * x |
| y = y * y |
| x2 := 1.0 |
| y2 := 1.0 |
| f := 1.0 |
| rn := 0.0 |
| d := 0.0 |
| for { |
| rn++ |
| f *= rn |
| rn++ |
| f *= rn |
| x2 *= x |
| y2 *= y |
| t := y2 + x2 |
| t /= f |
| d += t |
| |
| rn++ |
| f *= rn |
| rn++ |
| f *= rn |
| x2 *= x |
| y2 *= y |
| t = y2 - x2 |
| t /= f |
| d += t |
| if !(math.Abs(t/d) > MACHEP) { |
| // Caution: Use ! and > instead of <= for correct behavior if t/d is NaN. |
| // See issue 17577. |
| break |
| } |
| } |
| return d |
| } |
| |
| // Complex circular cotangent |
| // |
| // DESCRIPTION: |
| // |
| // If |
| // z = x + iy, |
| // |
| // then |
| // |
| // sin 2x - i sinh 2y |
| // w = --------------------. |
| // cosh 2y - cos 2x |
| // |
| // On the real axis, the denominator has zeros at even |
| // multiples of PI/2. Near these points it is evaluated |
| // by a Taylor series. |
| // |
| // ACCURACY: |
| // |
| // Relative error: |
| // arithmetic domain # trials peak rms |
| // DEC -10,+10 3000 6.5e-17 1.6e-17 |
| // IEEE -10,+10 30000 9.2e-16 1.2e-16 |
| // Also tested by ctan * ccot = 1 + i0. |
| |
| // Cot returns the cotangent of x. |
| func Cot(x complex128) complex128 { |
| d := math.Cosh(2*imag(x)) - math.Cos(2*real(x)) |
| if math.Abs(d) < 0.25 { |
| d = tanSeries(x) |
| } |
| if d == 0 { |
| return Inf() |
| } |
| return complex(math.Sin(2*real(x))/d, -math.Sinh(2*imag(x))/d) |
| } |