From 4209a9f65a461cfb184548e50ddb62fedd9f9e21 Mon Sep 17 00:00:00 2001 From: Brian Kessler Date: Fri, 1 Feb 2019 23:08:45 -0700 Subject: [PATCH] math/cmplx: handle special cases Implement special case handling and testing to ensure conformance with the C99 standard annex G.6 Complex arithmetic. Fixes #29320 Change-Id: Id72eb4c5a35d5a54b4b8690d2f7176ab11028f1b Reviewed-on: https://go-review.googlesource.com/c/go/+/220689 Reviewed-by: Robert Griesemer --- src/math/cmplx/abs.go | 3 +- src/math/cmplx/asin.go | 70 ++- src/math/cmplx/cmath_test.go | 886 +++++++++++++++++++++++++++++------ src/math/cmplx/exp.go | 17 + src/math/cmplx/log.go | 3 +- src/math/cmplx/sin.go | 52 ++ src/math/cmplx/sqrt.go | 2 + src/math/cmplx/tan.go | 20 + 8 files changed, 912 insertions(+), 141 deletions(-) diff --git a/src/math/cmplx/abs.go b/src/math/cmplx/abs.go index f3cd1073ed2..2f89d1bcfc7 100644 --- a/src/math/cmplx/abs.go +++ b/src/math/cmplx/abs.go @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. // Package cmplx provides basic constants and mathematical functions for -// complex numbers. +// complex numbers. Special case handling conforms to the C99 standard +// Annex G IEC 60559-compatible complex arithmetic. package cmplx import "math" diff --git a/src/math/cmplx/asin.go b/src/math/cmplx/asin.go index 062f324ce2c..30d019e9d47 100644 --- a/src/math/cmplx/asin.go +++ b/src/math/cmplx/asin.go @@ -49,8 +49,31 @@ import "math" // Asin returns the inverse sine of x. func Asin(x complex128) complex128 { - if imag(x) == 0 && math.Abs(real(x)) <= 1 { - return complex(math.Asin(real(x)), imag(x)) + switch re, im := real(x), imag(x); { + case im == 0 && math.Abs(re) <= 1: + return complex(math.Asin(re), im) + case re == 0 && math.Abs(im) <= 1: + return complex(re, math.Asinh(im)) + case math.IsNaN(im): + switch { + case re == 0: + return complex(re, math.NaN()) + case math.IsInf(re, 0): + return complex(math.NaN(), re) + default: + return NaN() + } + case math.IsInf(im, 0): + switch { + case math.IsNaN(re): + return x + case math.IsInf(re, 0): + return complex(math.Copysign(math.Pi/4, re), im) + default: + return complex(math.Copysign(0, re), im) + } + case math.IsInf(re, 0): + return complex(math.Copysign(math.Pi/2, re), math.Copysign(re, im)) } ct := complex(-imag(x), real(x)) // i * x xx := x * x @@ -62,8 +85,31 @@ func Asin(x complex128) complex128 { // Asinh returns the inverse hyperbolic sine of x. func Asinh(x complex128) complex128 { - if imag(x) == 0 && math.Abs(real(x)) <= 1 { - return complex(math.Asinh(real(x)), imag(x)) + switch re, im := real(x), imag(x); { + case im == 0 && math.Abs(re) <= 1: + return complex(math.Asinh(re), im) + case re == 0 && math.Abs(im) <= 1: + return complex(re, math.Asin(im)) + case math.IsInf(re, 0): + switch { + case math.IsInf(im, 0): + return complex(re, math.Copysign(math.Pi/4, im)) + case math.IsNaN(im): + return x + default: + return complex(re, math.Copysign(0.0, im)) + } + case math.IsNaN(re): + switch { + case im == 0: + return x + case math.IsInf(im, 0): + return complex(im, re) + default: + return NaN() + } + case math.IsInf(im, 0): + return complex(math.Copysign(im, re), math.Copysign(math.Pi/2, im)) } xx := x * x x1 := complex(1+real(xx), imag(xx)) // 1 + x*x @@ -91,6 +137,9 @@ func Acos(x complex128) complex128 { // Acosh returns the inverse hyperbolic cosine of x. func Acosh(x complex128) complex128 { + if x == 0 { + return complex(0, math.Copysign(math.Pi/2, imag(x))) + } w := Acos(x) if imag(w) <= 0 { return complex(-imag(w), real(w)) // i * w @@ -133,6 +182,19 @@ func Acosh(x complex128) complex128 { // Atan returns the inverse tangent of x. func Atan(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case im == 0: + return complex(math.Atan(re), im) + case re == 0 && math.Abs(im) <= 1: + return complex(re, math.Atanh(im)) + case math.IsInf(im, 0) || math.IsInf(re, 0): + if math.IsNaN(re) { + return complex(math.NaN(), math.Copysign(0, im)) + } + return complex(math.Copysign(math.Pi/2, re), math.Copysign(0, im)) + case math.IsNaN(re) || math.IsNaN(im): + return NaN() + } x2 := real(x) * real(x) a := 1 - x2 - imag(x)*imag(x) if a == 0 { diff --git a/src/math/cmplx/cmath_test.go b/src/math/cmplx/cmath_test.go index d934ba5e57a..3011e8327d5 100644 --- a/src/math/cmplx/cmath_test.go +++ b/src/math/cmplx/cmath_test.go @@ -320,48 +320,190 @@ var tanHuge = []complex128{ -0.76417695016604922, } -// special cases +// special cases conform to C99 standard appendix G.6 Complex arithmetic +var inf, nan = math.Inf(1), math.NaN() + var vcAbsSC = []complex128{ NaN(), } var absSC = []float64{ math.NaN(), } -var vcAcosSC = []complex128{ - NaN(), +var acosSC = []struct { + in, + want complex128 +}{ + // G.6.1.1 + {complex(zero, zero), + complex(math.Pi/2, -zero)}, + {complex(-zero, zero), + complex(math.Pi/2, -zero)}, + {complex(zero, nan), + complex(math.Pi/2, nan)}, + {complex(-zero, nan), + complex(math.Pi/2, nan)}, + {complex(1.0, inf), + complex(math.Pi/2, -inf)}, + {complex(1.0, nan), + NaN()}, + {complex(-inf, 1.0), + complex(math.Pi, -inf)}, + {complex(inf, 1.0), + complex(0.0, -inf)}, + {complex(-inf, inf), + complex(3*math.Pi/4, -inf)}, + {complex(inf, inf), + complex(math.Pi/4, -inf)}, + {complex(inf, nan), + complex(nan, -inf)}, // imaginary sign unspecified + {complex(-inf, nan), + complex(nan, inf)}, // imaginary sign unspecified + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(nan, -inf)}, + {NaN(), + NaN()}, } -var acosSC = []complex128{ - NaN(), +var acoshSC = []struct { + in, + want complex128 +}{ + // G.6.2.1 + {complex(zero, zero), + complex(zero, math.Pi/2)}, + {complex(-zero, zero), + complex(zero, math.Pi/2)}, + {complex(1.0, inf), + complex(inf, math.Pi/2)}, + {complex(1.0, nan), + NaN()}, + {complex(-inf, 1.0), + complex(inf, math.Pi)}, + {complex(inf, 1.0), + complex(inf, zero)}, + {complex(-inf, inf), + complex(inf, 3*math.Pi/4)}, + {complex(inf, inf), + complex(inf, math.Pi/4)}, + {complex(inf, nan), + complex(inf, nan)}, + {complex(-inf, nan), + complex(inf, nan)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(inf, nan)}, + {NaN(), + NaN()}, } -var vcAcoshSC = []complex128{ - NaN(), +var asinSC = []struct { + in, + want complex128 +}{ + // Derived from Asin(z) = -i * Asinh(i * z), G.6 #7 + {complex(zero, zero), + complex(zero, zero)}, + {complex(1.0, inf), + complex(0, inf)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1), + complex(math.Pi/2, inf)}, + {complex(inf, inf), + complex(math.Pi/4, inf)}, + {complex(inf, nan), + complex(nan, inf)}, // imaginary sign unspecified + {complex(nan, zero), + NaN()}, + {complex(nan, 1), + NaN()}, + {complex(nan, inf), + complex(nan, inf)}, + {NaN(), + NaN()}, } -var acoshSC = []complex128{ - NaN(), +var asinhSC = []struct { + in, + want complex128 +}{ + // G.6.2.2 + {complex(zero, zero), + complex(zero, zero)}, + {complex(1.0, inf), + complex(inf, math.Pi/2)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1.0), + complex(inf, zero)}, + {complex(inf, inf), + complex(inf, math.Pi/4)}, + {complex(inf, nan), + complex(inf, nan)}, + {complex(nan, zero), + complex(nan, zero)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(inf, nan)}, // sign of real part unspecified + {NaN(), + NaN()}, } -var vcAsinSC = []complex128{ - NaN(), +var atanSC = []struct { + in, + want complex128 +}{ + // Derived from Atan(z) = -i * Atanh(i * z), G.6 #7 + {complex(0, zero), + complex(0, zero)}, + {complex(0, nan), + NaN()}, + {complex(1.0, zero), + complex(math.Pi/4, zero)}, + {complex(1.0, inf), + complex(math.Pi/2, zero)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1), + complex(math.Pi/2, zero)}, + {complex(inf, inf), + complex(math.Pi/2, zero)}, + {complex(inf, nan), + complex(math.Pi/2, zero)}, + {complex(nan, 1), + NaN()}, + {complex(nan, inf), + complex(nan, zero)}, + {NaN(), + NaN()}, } -var asinSC = []complex128{ - NaN(), -} -var vcAsinhSC = []complex128{ - NaN(), -} -var asinhSC = []complex128{ - NaN(), -} -var vcAtanSC = []complex128{ - NaN(), -} -var atanSC = []complex128{ - NaN(), -} -var vcAtanhSC = []complex128{ - NaN(), -} -var atanhSC = []complex128{ - NaN(), +var atanhSC = []struct { + in, + want complex128 +}{ + // G.6.2.3 + {complex(zero, zero), + complex(zero, zero)}, + {complex(zero, nan), + complex(zero, nan)}, + {complex(1.0, zero), + complex(inf, zero)}, + {complex(1.0, inf), + complex(0, math.Pi/2)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1.0), + complex(zero, math.Pi/2)}, + {complex(inf, inf), + complex(zero, math.Pi/2)}, + {complex(inf, nan), + complex(0, nan)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(zero, math.Pi/2)}, // sign of real part not specified. + {NaN(), + NaN()}, } var vcConjSC = []complex128{ NaN(), @@ -369,23 +511,105 @@ var vcConjSC = []complex128{ var conjSC = []complex128{ NaN(), } -var vcCosSC = []complex128{ - NaN(), +var cosSC = []struct { + in, + want complex128 +}{ + // Derived from Cos(z) = Cosh(i * z), G.6 #7 + {complex(zero, zero), + complex(1.0, -zero)}, + {complex(zero, inf), + complex(inf, -zero)}, + {complex(zero, nan), + complex(nan, zero)}, // imaginary sign unspecified + {complex(1.0, inf), + complex(inf, -inf)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, zero), + complex(nan, -zero)}, + {complex(inf, 1.0), + NaN()}, + {complex(inf, inf), + complex(inf, nan)}, // real sign unspecified + {complex(inf, nan), + NaN()}, + {complex(nan, zero), + complex(nan, -zero)}, // imaginary sign unspecified + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(inf, nan)}, + {NaN(), + NaN()}, } -var cosSC = []complex128{ - NaN(), +var coshSC = []struct { + in, + want complex128 +}{ + // G.6.2.4 + {complex(zero, zero), + complex(1.0, zero)}, + {complex(zero, inf), + complex(nan, zero)}, // imaginary sign unspecified + {complex(zero, nan), + complex(nan, zero)}, // imaginary sign unspecified + {complex(1.0, inf), + NaN()}, + {complex(1.0, nan), + NaN()}, + {complex(inf, zero), + complex(inf, zero)}, + {complex(inf, 1.0), + complex(inf*math.Cos(1.0), inf*math.Sin(1.0))}, // +inf cis(y) + {complex(inf, inf), + complex(inf, nan)}, // real sign unspecified + {complex(inf, nan), + complex(inf, nan)}, + {complex(nan, zero), + complex(nan, zero)}, // imaginary sign unspecified + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + NaN()}, + {NaN(), + NaN()}, } -var vcCoshSC = []complex128{ - NaN(), -} -var coshSC = []complex128{ - NaN(), -} -var vcExpSC = []complex128{ - NaN(), -} -var expSC = []complex128{ - NaN(), +var expSC = []struct { + in, + want complex128 +}{ + // G.6.3.1 + {complex(zero, zero), + complex(1.0, zero)}, + {complex(-zero, zero), + complex(1.0, zero)}, + {complex(1.0, inf), + NaN()}, + {complex(1.0, nan), + NaN()}, + {complex(inf, zero), + complex(inf, zero)}, + {complex(-inf, 1.0), + complex(math.Copysign(0.0, math.Cos(1.0)), math.Copysign(0.0, math.Sin(1.0)))}, // +0 cis(y) + {complex(inf, 1.0), + complex(inf*math.Cos(1.0), inf*math.Sin(1.0))}, // +inf cis(y) + {complex(-inf, inf), + complex(zero, zero)}, // real and imaginary sign unspecified + {complex(inf, inf), + complex(inf, nan)}, // real sign unspecified + {complex(-inf, nan), + complex(zero, zero)}, // real and imaginary sign unspecified + {complex(inf, nan), + complex(inf, nan)}, // real sign unspecified + {complex(nan, zero), + complex(nan, zero)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + NaN()}, + {NaN(), + NaN()}, } var vcIsNaNSC = []complex128{ complex(math.Inf(-1), math.Inf(-1)), @@ -409,17 +633,70 @@ var isNaNSC = []bool{ false, true, } -var vcLogSC = []complex128{ - NaN(), + +var logSC = []struct { + in, + want complex128 +}{ + // G.6.3.2 + {complex(zero, zero), + complex(-inf, zero)}, + {complex(-zero, zero), + complex(-inf, math.Pi)}, + {complex(1.0, inf), + complex(inf, math.Pi/2)}, + {complex(1.0, nan), + NaN()}, + {complex(-inf, 1.0), + complex(inf, math.Pi)}, + {complex(inf, 1.0), + complex(inf, 0.0)}, + {complex(-inf, inf), + complex(inf, 3*math.Pi/4)}, + {complex(inf, inf), + complex(inf, math.Pi/4)}, + {complex(-inf, nan), + complex(inf, nan)}, + {complex(inf, nan), + complex(inf, nan)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(inf, nan)}, + {NaN(), + NaN()}, } -var logSC = []complex128{ - NaN(), -} -var vcLog10SC = []complex128{ - NaN(), -} -var log10SC = []complex128{ - NaN(), +var log10SC = []struct { + in, + want complex128 +}{ + // derived from Log special cases via Log10(x) = math.Log10E*Log(x) + {complex(zero, zero), + complex(-inf, zero)}, + {complex(-zero, zero), + complex(-inf, float64(math.Log10E)*float64(math.Pi))}, + {complex(1.0, inf), + complex(inf, float64(math.Log10E)*float64(math.Pi/2))}, + {complex(1.0, nan), + NaN()}, + {complex(-inf, 1.0), + complex(inf, float64(math.Log10E)*float64(math.Pi))}, + {complex(inf, 1.0), + complex(inf, 0.0)}, + {complex(-inf, inf), + complex(inf, float64(math.Log10E)*float64(3*math.Pi/4))}, + {complex(inf, inf), + complex(inf, float64(math.Log10E)*float64(math.Pi/4))}, + {complex(-inf, nan), + complex(inf, nan)}, + {complex(inf, nan), + complex(inf, nan)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(inf, nan)}, + {NaN(), + NaN()}, } var vcPolarSC = []complex128{ NaN(), @@ -435,35 +712,153 @@ var powSC = []complex128{ NaN(), NaN(), } -var vcSinSC = []complex128{ - NaN(), +var sinSC = []struct { + in, + want complex128 +}{ + // Derived from Sin(z) = -i * Sinh(i * z), G.6 #7 + {complex(zero, zero), + complex(zero, zero)}, + {complex(zero, inf), + complex(zero, inf)}, + {complex(zero, nan), + complex(zero, nan)}, + {complex(1.0, inf), + complex(inf, inf)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, zero), + complex(nan, zero)}, + {complex(inf, 1.0), + NaN()}, + {complex(inf, inf), + complex(nan, inf)}, + {complex(inf, nan), + NaN()}, + {complex(nan, zero), + complex(nan, zero)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(nan, inf)}, + {NaN(), + NaN()}, } -var sinSC = []complex128{ - NaN(), + +var sinhSC = []struct { + in, + want complex128 +}{ + // G.6.2.5 + {complex(zero, zero), + complex(zero, zero)}, + {complex(zero, inf), + complex(zero, nan)}, // real sign unspecified + {complex(zero, nan), + complex(zero, nan)}, // real sign unspecified + {complex(1.0, inf), + NaN()}, + {complex(1.0, nan), + NaN()}, + {complex(inf, zero), + complex(inf, zero)}, + {complex(inf, 1.0), + complex(inf*math.Cos(1.0), inf*math.Sin(1.0))}, // +inf cis(y) + {complex(inf, inf), + complex(inf, nan)}, // real sign unspecified + {complex(inf, nan), + complex(inf, nan)}, // real sign unspecified + {complex(nan, zero), + complex(nan, zero)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + NaN()}, + {NaN(), + NaN()}, } -var vcSinhSC = []complex128{ - NaN(), + +var sqrtSC = []struct { + in, + want complex128 +}{ + // G.6.4.2 + {complex(zero, zero), + complex(zero, zero)}, + {complex(-zero, zero), + complex(zero, zero)}, + {complex(1.0, inf), + complex(inf, inf)}, + {complex(nan, inf), + complex(inf, inf)}, + {complex(1.0, nan), + NaN()}, + {complex(-inf, 1.0), + complex(zero, inf)}, + {complex(inf, 1.0), + complex(inf, zero)}, + {complex(-inf, nan), + complex(nan, inf)}, // imaginary sign unspecified + {complex(inf, nan), + complex(inf, nan)}, + {complex(nan, 1.0), + NaN()}, + {NaN(), + NaN()}, } -var sinhSC = []complex128{ - NaN(), +var tanSC = []struct { + in, + want complex128 +}{ + // Derived from Tan(z) = -i * Tanh(i * z), G.6 #7 + {complex(zero, zero), + complex(zero, zero)}, + {complex(zero, nan), + complex(zero, nan)}, + {complex(1.0, inf), + complex(zero, 1.0)}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1.0), + NaN()}, + {complex(inf, inf), + complex(zero, 1.0)}, + {complex(inf, nan), + NaN()}, + {complex(nan, zero), + NaN()}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + complex(zero, 1.0)}, + {NaN(), + NaN()}, } -var vcSqrtSC = []complex128{ - NaN(), -} -var sqrtSC = []complex128{ - NaN(), -} -var vcTanSC = []complex128{ - NaN(), -} -var tanSC = []complex128{ - NaN(), -} -var vcTanhSC = []complex128{ - NaN(), -} -var tanhSC = []complex128{ - NaN(), +var tanhSC = []struct { + in, + want complex128 +}{ + // G.6.2.6 + {complex(zero, zero), + complex(zero, zero)}, + {complex(1.0, inf), + NaN()}, + {complex(1.0, nan), + NaN()}, + {complex(inf, 1.0), + complex(1.0, math.Copysign(0.0, math.Sin(2*1.0)))}, // 1 + i 0 sin(2y) + {complex(inf, inf), + complex(1.0, zero)}, // imaginary sign unspecified + {complex(inf, nan), + complex(1.0, zero)}, // imaginary sign unspecified + {complex(nan, zero), + complex(nan, zero)}, + {complex(nan, 1.0), + NaN()}, + {complex(nan, inf), + NaN()}, + {NaN(), + NaN()}, } // branch cut continuity checks @@ -525,13 +920,25 @@ func cTolerance(a, b complex128, e float64) bool { func cSoclose(a, b complex128, e float64) bool { return cTolerance(a, b, e) } func cVeryclose(a, b complex128) bool { return cTolerance(a, b, 4e-16) } func cAlike(a, b complex128) bool { - switch { - case IsNaN(a) && IsNaN(b): - return true - case a == b: - return math.Signbit(real(a)) == math.Signbit(real(b)) && math.Signbit(imag(a)) == math.Signbit(imag(b)) + var realAlike, imagAlike bool + if isExact(real(b)) { + realAlike = alike(real(a), real(b)) + } else { + // Allow non-exact special cases to have errors in ULP. + realAlike = veryclose(real(a), real(b)) } - return false + if isExact(imag(b)) { + imagAlike = alike(imag(a), imag(b)) + } else { + // Allow non-exact special cases to have errors in ULP. + imagAlike = veryclose(imag(a), imag(b)) + } + return realAlike && imagAlike +} +func isExact(x float64) bool { + // Special cases that should match exactly. Other cases are multiples + // of Pi that may not be last bit identical on all platforms. + return math.IsNaN(x) || math.IsInf(x, 0) || x == 0 || x == 1 || x == -1 } func TestAbs(t *testing.T) { @@ -552,9 +959,17 @@ func TestAcos(t *testing.T) { t.Errorf("Acos(%g) = %g, want %g", vc[i], f, acos[i]) } } - for i := 0; i < len(vcAcosSC); i++ { - if f := Acos(vcAcosSC[i]); !cAlike(acosSC[i], f) { - t.Errorf("Acos(%g) = %g, want %g", vcAcosSC[i], f, acosSC[i]) + for _, v := range acosSC { + if f := Acos(v.in); !cAlike(v.want, f) { + t.Errorf("Acos(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Acos(Conj(z)) == Conj(Acos(z)) + if f := Acos(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Acos(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) } } for _, pt := range branchPoints { @@ -569,10 +984,19 @@ func TestAcosh(t *testing.T) { t.Errorf("Acosh(%g) = %g, want %g", vc[i], f, acosh[i]) } } - for i := 0; i < len(vcAcoshSC); i++ { - if f := Acosh(vcAcoshSC[i]); !cAlike(acoshSC[i], f) { - t.Errorf("Acosh(%g) = %g, want %g", vcAcoshSC[i], f, acoshSC[i]) + for _, v := range acoshSC { + if f := Acosh(v.in); !cAlike(v.want, f) { + t.Errorf("Acosh(%g) = %g, want %g", v.in, f, v.want) } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Acosh(Conj(z)) == Conj(Acosh(z)) + if f := Acosh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Acosh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + } for _, pt := range branchPoints { if f0, f1 := Acosh(pt[0]), Acosh(pt[1]); !cVeryclose(f0, f1) { @@ -586,9 +1010,25 @@ func TestAsin(t *testing.T) { t.Errorf("Asin(%g) = %g, want %g", vc[i], f, asin[i]) } } - for i := 0; i < len(vcAsinSC); i++ { - if f := Asin(vcAsinSC[i]); !cAlike(asinSC[i], f) { - t.Errorf("Asin(%g) = %g, want %g", vcAsinSC[i], f, asinSC[i]) + for _, v := range asinSC { + if f := Asin(v.in); !cAlike(v.want, f) { + t.Errorf("Asin(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Asin(Conj(z)) == Asin(Sinh(z)) + if f := Asin(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Asin(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Asin(-z) == -Asin(z) + if f := Asin(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Asin(%g) = %g, want %g", -v.in, f, -v.want) } } for _, pt := range branchPoints { @@ -603,9 +1043,25 @@ func TestAsinh(t *testing.T) { t.Errorf("Asinh(%g) = %g, want %g", vc[i], f, asinh[i]) } } - for i := 0; i < len(vcAsinhSC); i++ { - if f := Asinh(vcAsinhSC[i]); !cAlike(asinhSC[i], f) { - t.Errorf("Asinh(%g) = %g, want %g", vcAsinhSC[i], f, asinhSC[i]) + for _, v := range asinhSC { + if f := Asinh(v.in); !cAlike(v.want, f) { + t.Errorf("Asinh(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Asinh(Conj(z)) == Asinh(Sinh(z)) + if f := Asinh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Asinh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Asinh(-z) == -Asinh(z) + if f := Asinh(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Asinh(%g) = %g, want %g", -v.in, f, -v.want) } } for _, pt := range branchPoints { @@ -620,9 +1076,25 @@ func TestAtan(t *testing.T) { t.Errorf("Atan(%g) = %g, want %g", vc[i], f, atan[i]) } } - for i := 0; i < len(vcAtanSC); i++ { - if f := Atan(vcAtanSC[i]); !cAlike(atanSC[i], f) { - t.Errorf("Atan(%g) = %g, want %g", vcAtanSC[i], f, atanSC[i]) + for _, v := range atanSC { + if f := Atan(v.in); !cAlike(v.want, f) { + t.Errorf("Atan(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Atan(Conj(z)) == Conj(Atan(z)) + if f := Atan(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Atan(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Atan(-z) == -Atan(z) + if f := Atan(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Atan(%g) = %g, want %g", -v.in, f, -v.want) } } for _, pt := range branchPoints { @@ -637,9 +1109,25 @@ func TestAtanh(t *testing.T) { t.Errorf("Atanh(%g) = %g, want %g", vc[i], f, atanh[i]) } } - for i := 0; i < len(vcAtanhSC); i++ { - if f := Atanh(vcAtanhSC[i]); !cAlike(atanhSC[i], f) { - t.Errorf("Atanh(%g) = %g, want %g", vcAtanhSC[i], f, atanhSC[i]) + for _, v := range atanhSC { + if f := Atanh(v.in); !cAlike(v.want, f) { + t.Errorf("Atanh(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Atanh(Conj(z)) == Conj(Atanh(z)) + if f := Atanh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Atanh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Atanh(-z) == -Atanh(z) + if f := Atanh(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Atanh(%g) = %g, want %g", -v.in, f, -v.want) } } for _, pt := range branchPoints { @@ -666,9 +1154,25 @@ func TestCos(t *testing.T) { t.Errorf("Cos(%g) = %g, want %g", vc[i], f, cos[i]) } } - for i := 0; i < len(vcCosSC); i++ { - if f := Cos(vcCosSC[i]); !cAlike(cosSC[i], f) { - t.Errorf("Cos(%g) = %g, want %g", vcCosSC[i], f, cosSC[i]) + for _, v := range cosSC { + if f := Cos(v.in); !cAlike(v.want, f) { + t.Errorf("Cos(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Cos(Conj(z)) == Cos(Cosh(z)) + if f := Cos(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Cos(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Cos(-z) == Cos(z) + if f := Cos(-v.in); !cAlike(v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Cos(%g) = %g, want %g", -v.in, f, v.want) } } } @@ -678,9 +1182,25 @@ func TestCosh(t *testing.T) { t.Errorf("Cosh(%g) = %g, want %g", vc[i], f, cosh[i]) } } - for i := 0; i < len(vcCoshSC); i++ { - if f := Cosh(vcCoshSC[i]); !cAlike(coshSC[i], f) { - t.Errorf("Cosh(%g) = %g, want %g", vcCoshSC[i], f, coshSC[i]) + for _, v := range coshSC { + if f := Cosh(v.in); !cAlike(v.want, f) { + t.Errorf("Cosh(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Cosh(Conj(z)) == Conj(Cosh(z)) + if f := Cosh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Cosh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Cosh(-z) == Cosh(z) + if f := Cosh(-v.in); !cAlike(v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Cosh(%g) = %g, want %g", -v.in, f, v.want) } } } @@ -690,9 +1210,17 @@ func TestExp(t *testing.T) { t.Errorf("Exp(%g) = %g, want %g", vc[i], f, exp[i]) } } - for i := 0; i < len(vcExpSC); i++ { - if f := Exp(vcExpSC[i]); !cAlike(expSC[i], f) { - t.Errorf("Exp(%g) = %g, want %g", vcExpSC[i], f, expSC[i]) + for _, v := range expSC { + if f := Exp(v.in); !cAlike(v.want, f) { + t.Errorf("Exp(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Exp(Conj(z)) == Exp(Cosh(z)) + if f := Exp(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Exp(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) } } } @@ -709,9 +1237,17 @@ func TestLog(t *testing.T) { t.Errorf("Log(%g) = %g, want %g", vc[i], f, log[i]) } } - for i := 0; i < len(vcLogSC); i++ { - if f := Log(vcLogSC[i]); !cAlike(logSC[i], f) { - t.Errorf("Log(%g) = %g, want %g", vcLogSC[i], f, logSC[i]) + for _, v := range logSC { + if f := Log(v.in); !cAlike(v.want, f) { + t.Errorf("Log(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Log(Conj(z)) == Conj(Log(z)) + if f := Log(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Log(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) } } for _, pt := range branchPoints { @@ -726,9 +1262,17 @@ func TestLog10(t *testing.T) { t.Errorf("Log10(%g) = %g, want %g", vc[i], f, log10[i]) } } - for i := 0; i < len(vcLog10SC); i++ { - if f := Log10(vcLog10SC[i]); !cAlike(log10SC[i], f) { - t.Errorf("Log10(%g) = %g, want %g", vcLog10SC[i], f, log10SC[i]) + for _, v := range log10SC { + if f := Log10(v.in); !cAlike(v.want, f) { + t.Errorf("Log10(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Log10(Conj(z)) == Conj(Log10(z)) + if f := Log10(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Log10(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) } } } @@ -793,9 +1337,25 @@ func TestSin(t *testing.T) { t.Errorf("Sin(%g) = %g, want %g", vc[i], f, sin[i]) } } - for i := 0; i < len(vcSinSC); i++ { - if f := Sin(vcSinSC[i]); !cAlike(sinSC[i], f) { - t.Errorf("Sin(%g) = %g, want %g", vcSinSC[i], f, sinSC[i]) + for _, v := range sinSC { + if f := Sin(v.in); !cAlike(v.want, f) { + t.Errorf("Sin(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Sin(Conj(z)) == Conj(Sin(z)) + if f := Sin(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Sinh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Sin(-z) == -Sin(z) + if f := Sin(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Sinh(%g) = %g, want %g", -v.in, f, -v.want) } } } @@ -805,9 +1365,25 @@ func TestSinh(t *testing.T) { t.Errorf("Sinh(%g) = %g, want %g", vc[i], f, sinh[i]) } } - for i := 0; i < len(vcSinhSC); i++ { - if f := Sinh(vcSinhSC[i]); !cAlike(sinhSC[i], f) { - t.Errorf("Sinh(%g) = %g, want %g", vcSinhSC[i], f, sinhSC[i]) + for _, v := range sinhSC { + if f := Sinh(v.in); !cAlike(v.want, f) { + t.Errorf("Sinh(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Sinh(Conj(z)) == Conj(Sinh(z)) + if f := Sinh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Sinh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Sinh(-z) == -Sinh(z) + if f := Sinh(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Sinh(%g) = %g, want %g", -v.in, f, -v.want) } } } @@ -817,9 +1393,17 @@ func TestSqrt(t *testing.T) { t.Errorf("Sqrt(%g) = %g, want %g", vc[i], f, sqrt[i]) } } - for i := 0; i < len(vcSqrtSC); i++ { - if f := Sqrt(vcSqrtSC[i]); !cAlike(sqrtSC[i], f) { - t.Errorf("Sqrt(%g) = %g, want %g", vcSqrtSC[i], f, sqrtSC[i]) + for _, v := range sqrtSC { + if f := Sqrt(v.in); !cAlike(v.want, f) { + t.Errorf("Sqrt(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Sqrt(Conj(z)) == Conj(Sqrt(z)) + if f := Sqrt(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Sqrt(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) } } for _, pt := range branchPoints { @@ -834,9 +1418,25 @@ func TestTan(t *testing.T) { t.Errorf("Tan(%g) = %g, want %g", vc[i], f, tan[i]) } } - for i := 0; i < len(vcTanSC); i++ { - if f := Tan(vcTanSC[i]); !cAlike(tanSC[i], f) { - t.Errorf("Tan(%g) = %g, want %g", vcTanSC[i], f, tanSC[i]) + for _, v := range tanSC { + if f := Tan(v.in); !cAlike(v.want, f) { + t.Errorf("Tan(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Tan(Conj(z)) == Conj(Tan(z)) + if f := Tan(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Tan(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Tan(-z) == -Tan(z) + if f := Tan(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Tan(%g) = %g, want %g", -v.in, f, -v.want) } } } @@ -846,9 +1446,25 @@ func TestTanh(t *testing.T) { t.Errorf("Tanh(%g) = %g, want %g", vc[i], f, tanh[i]) } } - for i := 0; i < len(vcTanhSC); i++ { - if f := Tanh(vcTanhSC[i]); !cAlike(tanhSC[i], f) { - t.Errorf("Tanh(%g) = %g, want %g", vcTanhSC[i], f, tanhSC[i]) + for _, v := range tanhSC { + if f := Tanh(v.in); !cAlike(v.want, f) { + t.Errorf("Tanh(%g) = %g, want %g", v.in, f, v.want) + } + if math.IsNaN(imag(v.in)) || math.IsNaN(imag(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Tanh(Conj(z)) == Conj(Tanh(z)) + if f := Tanh(Conj(v.in)); !cAlike(Conj(v.want), f) && !cAlike(v.in, Conj(v.in)) { + t.Errorf("Tanh(%g) = %g, want %g", Conj(v.in), f, Conj(v.want)) + } + if math.IsNaN(real(v.in)) || math.IsNaN(real(v.want)) { + // Negating NaN is undefined with regard to the sign bit produced. + continue + } + // Tanh(-z) == -Tanh(z) + if f := Tanh(-v.in); !cAlike(-v.want, f) && !cAlike(v.in, -v.in) { + t.Errorf("Tanh(%g) = %g, want %g", -v.in, f, -v.want) } } } diff --git a/src/math/cmplx/exp.go b/src/math/cmplx/exp.go index 485ed2c78d9..d5d0a5d470c 100644 --- a/src/math/cmplx/exp.go +++ b/src/math/cmplx/exp.go @@ -49,6 +49,23 @@ import "math" // Exp returns e**x, the base-e exponential of x. func Exp(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case math.IsInf(re, 0): + switch { + case re > 0 && im == 0: + return x + case math.IsInf(im, 0) || math.IsNaN(im): + if re < 0 { + return complex(0, math.Copysign(0, im)) + } else { + return complex(math.Inf(1.0), math.NaN()) + } + } + case math.IsNaN(re): + if im == 0 { + return complex(math.NaN(), im) + } + } r := math.Exp(real(x)) s, c := math.Sincos(imag(x)) return complex(r*c, r*s) diff --git a/src/math/cmplx/log.go b/src/math/cmplx/log.go index 881a064d8b8..fd39c76cde1 100644 --- a/src/math/cmplx/log.go +++ b/src/math/cmplx/log.go @@ -60,5 +60,6 @@ func Log(x complex128) complex128 { // Log10 returns the decimal logarithm of x. func Log10(x complex128) complex128 { - return math.Log10E * Log(x) + z := Log(x) + return complex(math.Log10E*real(z), math.Log10E*imag(z)) } diff --git a/src/math/cmplx/sin.go b/src/math/cmplx/sin.go index 2c57536edfc..febac0e0bbd 100644 --- a/src/math/cmplx/sin.go +++ b/src/math/cmplx/sin.go @@ -51,6 +51,19 @@ import "math" // Sin returns the sine of x. func Sin(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case im == 0 && (math.IsInf(re, 0) || math.IsNaN(re)): + return complex(math.NaN(), im) + case math.IsInf(im, 0): + switch { + case re == 0: + return x + case math.IsInf(re, 0) || math.IsNaN(re): + return complex(math.NaN(), im) + } + case re == 0 && math.IsNaN(im): + return x + } s, c := math.Sincos(real(x)) sh, ch := sinhcosh(imag(x)) return complex(s*ch, c*sh) @@ -71,6 +84,19 @@ func Sin(x complex128) complex128 { // Sinh returns the hyperbolic sine of x. func Sinh(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case re == 0 && (math.IsInf(im, 0) || math.IsNaN(im)): + return complex(re, math.NaN()) + case math.IsInf(re, 0): + switch { + case im == 0: + return complex(re, im) + case math.IsInf(im, 0) || math.IsNaN(im): + return complex(re, math.NaN()) + } + case im == 0 && math.IsNaN(re): + return complex(math.NaN(), im) + } s, c := math.Sincos(imag(x)) sh, ch := sinhcosh(real(x)) return complex(c*sh, s*ch) @@ -96,6 +122,19 @@ func Sinh(x complex128) complex128 { // Cos returns the cosine of x. func Cos(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case im == 0 && (math.IsInf(re, 0) || math.IsNaN(re)): + return complex(math.NaN(), -im*math.Copysign(0, re)) + case math.IsInf(im, 0): + switch { + case re == 0: + return complex(math.Inf(1), -re*math.Copysign(0, im)) + case math.IsInf(re, 0) || math.IsNaN(re): + return complex(math.Inf(1), math.NaN()) + } + case re == 0 && math.IsNaN(im): + return complex(math.NaN(), 0) + } s, c := math.Sincos(real(x)) sh, ch := sinhcosh(imag(x)) return complex(c*ch, -s*sh) @@ -115,6 +154,19 @@ func Cos(x complex128) complex128 { // Cosh returns the hyperbolic cosine of x. func Cosh(x complex128) complex128 { + switch re, im := real(x), imag(x); { + case re == 0 && (math.IsInf(im, 0) || math.IsNaN(im)): + return complex(math.NaN(), re*math.Copysign(0, im)) + case math.IsInf(re, 0): + switch { + case im == 0: + return complex(math.Inf(1), im*math.Copysign(0, re)) + case math.IsInf(im, 0) || math.IsNaN(im): + return complex(math.Inf(1), math.NaN()) + } + case im == 0 && math.IsNaN(re): + return complex(math.NaN(), im) + } s, c := math.Sincos(imag(x)) sh, ch := sinhcosh(real(x)) return complex(c*ch, s*sh) diff --git a/src/math/cmplx/sqrt.go b/src/math/cmplx/sqrt.go index 741e5a88658..eddce2fdfbd 100644 --- a/src/math/cmplx/sqrt.go +++ b/src/math/cmplx/sqrt.go @@ -65,6 +65,8 @@ func Sqrt(x complex128) complex128 { return complex(0, math.Copysign(math.Sqrt(-real(x)), imag(x))) } return complex(math.Sqrt(real(x)), imag(x)) + } else if math.IsInf(imag(x), 0) { + return complex(math.Inf(1.0), imag(x)) } if real(x) == 0 { if imag(x) < 0 { diff --git a/src/math/cmplx/tan.go b/src/math/cmplx/tan.go index 714fb8c45ba..67a1133a6f4 100644 --- a/src/math/cmplx/tan.go +++ b/src/math/cmplx/tan.go @@ -60,6 +60,16 @@ import ( // 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) @@ -84,6 +94,16 @@ func Tan(x complex128) complex128 { // 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()