Commit 68dce429 authored by Brian Kessler's avatar Brian Kessler Committed by Robert Griesemer

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: Ieb0527191dd7fdea5b1aecb42b9e23aae3f74260
Reviewed-on: https://go-review.googlesource.com/c/go/+/169501
Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 3c47eada
......@@ -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"
......
......@@ -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,17 @@ 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))
}
x2 := real(x) * real(x)
a := 1 - x2 - imag(x)*imag(x)
if a == 0 {
......
......@@ -291,48 +291,190 @@ var tanh = []complex128{
(-1.0000000491604982429364892e+00 - 2.901873195374433112227349e-08i),
}
// 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 = []complex128{
NaN(),
}
var vcAcoshSC = []complex128{
NaN(),
}
var acoshSC = []complex128{
NaN(),
}
var vcAsinSC = []complex128{
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 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 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 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 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 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 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(),
......@@ -340,23 +482,105 @@ var vcConjSC = []complex128{
var conjSC = []complex128{
NaN(),
}
var vcCosSC = []complex128{
NaN(),
}
var cosSC = []complex128{
NaN(),
}
var vcCoshSC = []complex128{
NaN(),
}
var coshSC = []complex128{
NaN(),
}
var vcExpSC = []complex128{
NaN(),
}
var expSC = []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 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 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)),
......@@ -380,17 +604,70 @@ var isNaNSC = []bool{
false,
true,
}
var vcLogSC = []complex128{
NaN(),
}
var logSC = []complex128{
NaN(),
}
var vcLog10SC = []complex128{
NaN(),
}
var log10SC = []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 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(),
......@@ -406,35 +683,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 vcSinhSC = []complex128{
NaN(),
}
var sinhSC = []complex128{
NaN(),
}
var vcSqrtSC = []complex128{
NaN(),
}
var sqrtSC = []complex128{
NaN(),
}
var vcTanSC = []complex128{
NaN(),
}
var tanSC = []complex128{
NaN(),
}
var vcTanhSC = []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 tanhSC = []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 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 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
......@@ -496,13 +891,7 @@ 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))
}
return false
return alike(real(a), real(b)) && alike(imag(a), imag(b))
}
func TestAbs(t *testing.T) {
......@@ -523,9 +912,13 @@ 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)
}
// 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 {
......@@ -540,10 +933,15 @@ 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)
}
// 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) {
......@@ -557,9 +955,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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 {
......@@ -574,9 +984,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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 {
......@@ -591,9 +1013,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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 {
......@@ -608,9 +1042,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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 {
......@@ -637,9 +1083,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......@@ -649,9 +1107,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......@@ -661,9 +1131,13 @@ 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)
}
// 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))
}
}
}
......@@ -680,9 +1154,13 @@ 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)
}
// 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 {
......@@ -697,9 +1175,13 @@ 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)
}
// 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))
}
}
}
......@@ -764,9 +1246,22 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......@@ -776,9 +1271,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......@@ -788,9 +1295,13 @@ 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)
}
// 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 {
......@@ -805,9 +1316,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......@@ -817,9 +1340,21 @@ 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 cAlike(-v.in, Conj(v.in)) && !cAlike(-v.want, Conj(v.want)) {
// The following conditions can't simultaneously be satisfied for this input.
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))
}
// 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)
}
}
}
......
......@@ -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)
......
......@@ -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))
}
......@@ -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)
......
......@@ -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 {
......
......@@ -57,6 +57,16 @@ import "math"
// 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)
......@@ -81,6 +91,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()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment