mirror of
https://github.com/gonum/gonum.git
synced 2025-10-24 15:43:07 +08:00
cmplxs/cscalar: new package containing non-vector functions from cmplxs
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
||||
"math"
|
||||
"math/cmplx"
|
||||
|
||||
"gonum.org/v1/gonum/floats/scalar"
|
||||
"gonum.org/v1/gonum/cmplxs/cscalar"
|
||||
"gonum.org/v1/gonum/internal/asm/c128"
|
||||
)
|
||||
|
||||
@@ -200,7 +200,7 @@ func EqualApprox(s1, s2 []complex128, tol float64) bool {
|
||||
return false
|
||||
}
|
||||
for i, a := range s1 {
|
||||
if !EqualWithinAbsOrRel(a, s2[i], tol, tol) {
|
||||
if !cscalar.EqualWithinAbsOrRel(a, s2[i], tol, tol) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -221,40 +221,6 @@ func EqualFunc(s1, s2 []complex128, f func(complex128, complex128) bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// EqualWithinAbs returns true if a and b have an absolute difference
|
||||
// of less than tol.
|
||||
func EqualWithinAbs(a, b complex128, tol float64) bool {
|
||||
return a == b || cmplx.Abs(a-b) <= tol
|
||||
}
|
||||
|
||||
const minNormalFloat64 = 2.2250738585072014e-308
|
||||
|
||||
// EqualWithinRel returns true if the difference between a and b
|
||||
// is not greater than tol times the greater absolute value of a and b,
|
||||
// abs(a-b) <= tol * max(abs(a), abs(b)).
|
||||
func EqualWithinRel(a, b complex128, tol float64) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
|
||||
delta := cmplx.Abs(a - b)
|
||||
if delta <= minNormalFloat64 {
|
||||
return delta <= tol*minNormalFloat64
|
||||
}
|
||||
// We depend on the division in this relationship to identify
|
||||
// infinities.
|
||||
return delta/math.Max(cmplx.Abs(a), cmplx.Abs(b)) <= tol
|
||||
}
|
||||
|
||||
// EqualWithinAbsOrRel returns true if parts of a and b are equal to within
|
||||
// the absolute tolerance.
|
||||
func EqualWithinAbsOrRel(a, b complex128, absTol, relTol float64) bool {
|
||||
if EqualWithinAbs(a, b, absTol) {
|
||||
return true
|
||||
}
|
||||
return EqualWithinRel(a, b, relTol)
|
||||
}
|
||||
|
||||
// EqualLengths returns true if all of the slices have equal length,
|
||||
// and false otherwise. Returns true if there are no input slices.
|
||||
func EqualLengths(slices ...[]complex128) bool {
|
||||
@@ -487,19 +453,6 @@ func Norm(s []complex128, L float64) float64 {
|
||||
}
|
||||
}
|
||||
|
||||
// ParseWithNA converts the string s to a complex128 in v.
|
||||
// If s equals missing, w is returned as 0, otherwise 1.
|
||||
func ParseWithNA(s, missing string) (v complex128, w float64, err error) {
|
||||
if s == missing {
|
||||
return 0, 0, nil
|
||||
}
|
||||
v, err = parse(s)
|
||||
if err == nil {
|
||||
w = 1
|
||||
}
|
||||
return v, w, err
|
||||
}
|
||||
|
||||
// Prod returns the product of the elements of the slice.
|
||||
// Returns 1 if len(s) = 0.
|
||||
func Prod(s []complex128) complex128 {
|
||||
@@ -517,36 +470,6 @@ func Reverse(s []complex128) {
|
||||
}
|
||||
}
|
||||
|
||||
// Round returns the half away from zero rounded value of x with prec precision.
|
||||
//
|
||||
// Special cases are:
|
||||
// Round(±0) = +0
|
||||
// Round(±Inf) = ±Inf
|
||||
// Round(NaN) = NaN
|
||||
func Round(x complex128, prec int) complex128 {
|
||||
if x == 0 {
|
||||
// Make sure zero is returned
|
||||
// without the negative bit set.
|
||||
return 0
|
||||
}
|
||||
return complex(scalar.Round(real(x), prec), scalar.Round(imag(x), prec))
|
||||
}
|
||||
|
||||
// RoundEven returns the half even rounded value of x with prec precision.
|
||||
//
|
||||
// Special cases are:
|
||||
// RoundEven(±0) = +0
|
||||
// RoundEven(±Inf) = ±Inf
|
||||
// RoundEven(NaN) = NaN
|
||||
func RoundEven(x complex128, prec int) complex128 {
|
||||
if x == 0 {
|
||||
// Make sure zero is returned
|
||||
// without the negative bit set.
|
||||
return 0
|
||||
}
|
||||
return complex(scalar.RoundEven(real(x), prec), scalar.RoundEven(imag(x), prec))
|
||||
}
|
||||
|
||||
// Same returns true if the input slices have the same length and all elements
|
||||
// have the same value with NaN treated as the same.
|
||||
func Same(s, t []complex128) bool {
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/exp/rand"
|
||||
|
||||
"gonum.org/v1/gonum/cmplxs/cscalar"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,7 +34,7 @@ func areSlicesSame(t *testing.T, truth, comp []complex128, str string) {
|
||||
ok := len(truth) == len(comp)
|
||||
if ok {
|
||||
for i, a := range truth {
|
||||
if !EqualWithinAbsOrRel(a, comp[i], EqTolerance, EqTolerance) && !same(a, comp[i]) {
|
||||
if !cscalar.EqualWithinAbsOrRel(a, comp[i], EqTolerance, EqTolerance) && !cscalar.Same(a, comp[i]) {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
@@ -365,103 +367,6 @@ func TestEqualFunc(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqualsRelative(t *testing.T) {
|
||||
equalityTests := []struct {
|
||||
a, b float64
|
||||
tol float64
|
||||
equal bool
|
||||
}{
|
||||
{1000000, 1000001, 0, true},
|
||||
{1000001, 1000000, 0, true},
|
||||
{10000, 10001, 0, false},
|
||||
{10001, 10000, 0, false},
|
||||
{-1000000, -1000001, 0, true},
|
||||
{-1000001, -1000000, 0, true},
|
||||
{-10000, -10001, 0, false},
|
||||
{-10001, -10000, 0, false},
|
||||
{1.0000001, 1.0000002, 0, true},
|
||||
{1.0000002, 1.0000001, 0, true},
|
||||
{1.0002, 1.0001, 0, false},
|
||||
{1.0001, 1.0002, 0, false},
|
||||
{-1.000001, -1.000002, 0, true},
|
||||
{-1.000002, -1.000001, 0, true},
|
||||
{-1.0001, -1.0002, 0, false},
|
||||
{-1.0002, -1.0001, 0, false},
|
||||
{0.000000001000001, 0.000000001000002, 0, true},
|
||||
{0.000000001000002, 0.000000001000001, 0, true},
|
||||
{0.000000000001002, 0.000000000001001, 0, false},
|
||||
{0.000000000001001, 0.000000000001002, 0, false},
|
||||
{-0.000000001000001, -0.000000001000002, 0, true},
|
||||
{-0.000000001000002, -0.000000001000001, 0, true},
|
||||
{-0.000000000001002, -0.000000000001001, 0, false},
|
||||
{-0.000000000001001, -0.000000000001002, 0, false},
|
||||
{0, 0, 0, true},
|
||||
{0, -0, 0, true},
|
||||
{-0, -0, 0, true},
|
||||
{0.00000001, 0, 0, false},
|
||||
{0, 0.00000001, 0, false},
|
||||
{-0.00000001, 0, 0, false},
|
||||
{0, -0.00000001, 0, false},
|
||||
{0, 1e-310, 0.01, true},
|
||||
{1e-310, 0, 0.01, true},
|
||||
{1e-310, 0, 0.000001, false},
|
||||
{0, 1e-310, 0.000001, false},
|
||||
{0, -1e-310, 0.1, true},
|
||||
{-1e-310, 0, 0.1, true},
|
||||
{-1e-310, 0, 0.00000001, false},
|
||||
{0, -1e-310, 0.00000001, false},
|
||||
{math.Inf(1), math.Inf(1), 0, true},
|
||||
{math.Inf(1), math.MaxFloat64, 0, false},
|
||||
{math.NaN(), math.NaN(), 0, false},
|
||||
{math.NaN(), 0, 0, false},
|
||||
{-0, math.NaN(), 0, false},
|
||||
{math.NaN(), -0, 0, false},
|
||||
{0, math.NaN(), 0, false},
|
||||
{math.NaN(), math.Inf(1), 0, false},
|
||||
{math.Inf(1), math.NaN(), 0, false},
|
||||
{math.NaN(), math.MaxFloat64, 0, false},
|
||||
{math.MaxFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), -math.MaxFloat64, 0, false},
|
||||
{-math.MaxFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), -math.SmallestNonzeroFloat64, 0, false},
|
||||
{-math.SmallestNonzeroFloat64, math.NaN(), 0, false},
|
||||
{1.000000001, -1.0, 0, false},
|
||||
{-1.0, 1.000000001, 0, false},
|
||||
{-1.000000001, 1.0, 0, false},
|
||||
{1.0, -1.000000001, 0, false},
|
||||
{10 * math.SmallestNonzeroFloat64, 10 * -math.SmallestNonzeroFloat64, 0, true},
|
||||
{1e11 * math.SmallestNonzeroFloat64, 1e11 * -math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, 0, true},
|
||||
{-math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64, 0, true},
|
||||
{math.SmallestNonzeroFloat64, 0, 0, true},
|
||||
{0, math.SmallestNonzeroFloat64, 0, true},
|
||||
{-math.SmallestNonzeroFloat64, 0, 0, true},
|
||||
{0, -math.SmallestNonzeroFloat64, 0, true},
|
||||
{0.000000001, -math.SmallestNonzeroFloat64, 0, false},
|
||||
{0.000000001, math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, 0.000000001, 0, false},
|
||||
{-math.SmallestNonzeroFloat64, 0.000000001, 0, false},
|
||||
}
|
||||
for _, ts := range equalityTests {
|
||||
if ts.tol == 0 {
|
||||
ts.tol = 1e-5
|
||||
}
|
||||
|
||||
for _, comp := range []struct{ a, b complex128 }{
|
||||
{a: complex(ts.a, 0), b: complex(ts.b, 0)},
|
||||
{a: complex(0, ts.a), b: complex(0, ts.b)},
|
||||
{a: complex(ts.a, ts.a), b: complex(ts.b, ts.b)},
|
||||
} {
|
||||
if equal := EqualWithinRel(comp.a, comp.b, ts.tol); equal != ts.equal {
|
||||
t.Errorf("Relative equality of %g and %g with tolerance %g returned: %v. Expected: %v",
|
||||
comp.a, comp.b, ts.tol, equal, ts.equal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqualLengths(t *testing.T) {
|
||||
s1 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i}
|
||||
s2 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i}
|
||||
@@ -641,7 +546,7 @@ func TestMaxAbsAndIdx(t *testing.T) {
|
||||
t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx)
|
||||
}
|
||||
val := MaxAbs(test.in)
|
||||
if !same(val, test.wantVal) {
|
||||
if !cscalar.Same(val, test.wantVal) {
|
||||
t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal)
|
||||
}
|
||||
}
|
||||
@@ -684,7 +589,7 @@ func TestMinAbsAndIdx(t *testing.T) {
|
||||
t.Errorf("Wrong index "+test.desc+": got:%d want:%d", ind, test.wantIdx)
|
||||
}
|
||||
val := MinAbs(test.in)
|
||||
if !same(val, test.wantVal) {
|
||||
if !cscalar.Same(val, test.wantVal) {
|
||||
t.Errorf("Wrong value "+test.desc+": got:%f want:%f", val, test.wantVal)
|
||||
}
|
||||
}
|
||||
@@ -893,134 +798,6 @@ func TestReverse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRound(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
x complex128
|
||||
prec int
|
||||
want complex128
|
||||
}{
|
||||
{x: 0, prec: 1, want: 0},
|
||||
{x: cmplx.Inf(), prec: 1, want: cmplx.Inf()},
|
||||
{x: cmplx.NaN(), prec: 1, want: cmplx.NaN()},
|
||||
{x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0},
|
||||
{x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2},
|
||||
{x: 1 << 64, prec: 1, want: 1 << 64},
|
||||
{x: 454.4445, prec: 3, want: 454.445},
|
||||
{x: 454.44445, prec: 4, want: 454.4445},
|
||||
{x: 0.42499, prec: 4, want: 0.425},
|
||||
{x: 0.42599, prec: 4, want: 0.426},
|
||||
{x: 0.424999999999993, prec: 2, want: 0.42},
|
||||
{x: 0.425, prec: 2, want: 0.43},
|
||||
{x: 0.425000000000001, prec: 2, want: 0.43},
|
||||
{x: 123.4244999999999, prec: 3, want: 123.424},
|
||||
{x: 123.4245, prec: 3, want: 123.425},
|
||||
{x: 123.4245000000001, prec: 3, want: 123.425},
|
||||
|
||||
{x: 454.45, prec: 0, want: 454},
|
||||
{x: 454.45, prec: 1, want: 454.5},
|
||||
{x: 454.45, prec: 2, want: 454.45},
|
||||
{x: 454.45, prec: 3, want: 454.45},
|
||||
{x: 454.445, prec: 0, want: 454},
|
||||
{x: 454.445, prec: 1, want: 454.4},
|
||||
{x: 454.445, prec: 2, want: 454.45},
|
||||
{x: 454.445, prec: 3, want: 454.445},
|
||||
{x: 454.445, prec: 4, want: 454.445},
|
||||
{x: 454.55, prec: 0, want: 455},
|
||||
{x: 454.55, prec: 1, want: 454.6},
|
||||
{x: 454.55, prec: 2, want: 454.55},
|
||||
{x: 454.55, prec: 3, want: 454.55},
|
||||
{x: 454.455, prec: 0, want: 454},
|
||||
{x: 454.455, prec: 1, want: 454.5},
|
||||
{x: 454.455, prec: 2, want: 454.46},
|
||||
{x: 454.455, prec: 3, want: 454.455},
|
||||
{x: 454.455, prec: 4, want: 454.455},
|
||||
|
||||
// Negative precision.
|
||||
{x: 454.45, prec: -1, want: 450},
|
||||
{x: 454.45, prec: -2, want: 500},
|
||||
{x: 500, prec: -3, want: 1000},
|
||||
{x: 500, prec: -4, want: 0},
|
||||
{x: 1500, prec: -3, want: 2000},
|
||||
{x: 1500, prec: -4, want: 0},
|
||||
} {
|
||||
for _, sign := range []complex128{1, -1} {
|
||||
got := Round(sign*test.x, test.prec)
|
||||
want := sign * test.want
|
||||
if want == 0 {
|
||||
want = 0
|
||||
}
|
||||
// FIXME(kortschak): Complexify this.
|
||||
if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) {
|
||||
t.Errorf("unexpected result for Round(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundEven(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
x complex128
|
||||
prec int
|
||||
want complex128
|
||||
}{
|
||||
{x: 0, prec: 1, want: 0},
|
||||
{x: cmplx.Inf(), prec: 1, want: cmplx.Inf()},
|
||||
{x: cmplx.NaN(), prec: 1, want: cmplx.NaN()},
|
||||
{x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0},
|
||||
{x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2},
|
||||
{x: 1 << 64, prec: 1, want: 1 << 64},
|
||||
{x: 454.4445, prec: 3, want: 454.444},
|
||||
{x: 454.44445, prec: 4, want: 454.4444},
|
||||
{x: 0.42499, prec: 4, want: 0.425},
|
||||
{x: 0.42599, prec: 4, want: 0.426},
|
||||
{x: 0.424999999999993, prec: 2, want: 0.42},
|
||||
{x: 0.425, prec: 2, want: 0.42},
|
||||
{x: 0.425000000000001, prec: 2, want: 0.43},
|
||||
{x: 123.4244999999999, prec: 3, want: 123.424},
|
||||
{x: 123.4245, prec: 3, want: 123.424},
|
||||
{x: 123.4245000000001, prec: 3, want: 123.425},
|
||||
|
||||
{x: 454.45, prec: 0, want: 454},
|
||||
{x: 454.45, prec: 1, want: 454.4},
|
||||
{x: 454.45, prec: 2, want: 454.45},
|
||||
{x: 454.45, prec: 3, want: 454.45},
|
||||
{x: 454.445, prec: 0, want: 454},
|
||||
{x: 454.445, prec: 1, want: 454.4},
|
||||
{x: 454.445, prec: 2, want: 454.44},
|
||||
{x: 454.445, prec: 3, want: 454.445},
|
||||
{x: 454.445, prec: 4, want: 454.445},
|
||||
{x: 454.55, prec: 0, want: 455},
|
||||
{x: 454.55, prec: 1, want: 454.6},
|
||||
{x: 454.55, prec: 2, want: 454.55},
|
||||
{x: 454.55, prec: 3, want: 454.55},
|
||||
{x: 454.455, prec: 0, want: 454},
|
||||
{x: 454.455, prec: 1, want: 454.5},
|
||||
{x: 454.455, prec: 2, want: 454.46},
|
||||
{x: 454.455, prec: 3, want: 454.455},
|
||||
{x: 454.455, prec: 4, want: 454.455},
|
||||
|
||||
// Negative precision.
|
||||
{x: 454.45, prec: -1, want: 450},
|
||||
{x: 454.45, prec: -2, want: 500},
|
||||
{x: 500, prec: -3, want: 0},
|
||||
{x: 500, prec: -4, want: 0},
|
||||
{x: 1500, prec: -3, want: 2000},
|
||||
{x: 1500, prec: -4, want: 0},
|
||||
} {
|
||||
for _, sign := range []complex128{1, -1} {
|
||||
got := RoundEven(sign*test.x, test.prec)
|
||||
want := sign * test.want
|
||||
if want == 0 {
|
||||
want = 0
|
||||
}
|
||||
// FIXME(kortschak): Complexify this.
|
||||
if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) {
|
||||
t.Errorf("unexpected result for RoundEven(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSame(t *testing.T) {
|
||||
s1 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i}
|
||||
s2 := []complex128{1 + 1i, 2 + 2i, 3 + 3i, 4 + 4i}
|
||||
|
||||
94
cmplxs/cscalar/cscalar.go
Normal file
94
cmplxs/cscalar/cscalar.go
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cscalar
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/cmplx"
|
||||
|
||||
"gonum.org/v1/gonum/floats/scalar"
|
||||
)
|
||||
|
||||
// EqualWithinAbs returns true if a and b have an absolute difference
|
||||
// of less than tol.
|
||||
func EqualWithinAbs(a, b complex128, tol float64) bool {
|
||||
return a == b || cmplx.Abs(a-b) <= tol
|
||||
}
|
||||
|
||||
const minNormalFloat64 = 2.2250738585072014e-308
|
||||
|
||||
// EqualWithinRel returns true if the difference between a and b
|
||||
// is not greater than tol times the greater absolute value of a and b,
|
||||
// abs(a-b) <= tol * max(abs(a), abs(b)).
|
||||
func EqualWithinRel(a, b complex128, tol float64) bool {
|
||||
if a == b {
|
||||
return true
|
||||
}
|
||||
|
||||
delta := cmplx.Abs(a - b)
|
||||
if delta <= minNormalFloat64 {
|
||||
return delta <= tol*minNormalFloat64
|
||||
}
|
||||
// We depend on the division in this relationship to identify
|
||||
// infinities.
|
||||
return delta/math.Max(cmplx.Abs(a), cmplx.Abs(b)) <= tol
|
||||
}
|
||||
|
||||
// EqualWithinAbsOrRel returns true if parts of a and b are equal to within
|
||||
// the absolute tolerance.
|
||||
func EqualWithinAbsOrRel(a, b complex128, absTol, relTol float64) bool {
|
||||
if EqualWithinAbs(a, b, absTol) {
|
||||
return true
|
||||
}
|
||||
return EqualWithinRel(a, b, relTol)
|
||||
}
|
||||
|
||||
// ParseWithNA converts the string s to a complex128 in v.
|
||||
// If s equals missing, w is returned as 0, otherwise 1.
|
||||
func ParseWithNA(s, missing string) (v complex128, w float64, err error) {
|
||||
if s == missing {
|
||||
return 0, 0, nil
|
||||
}
|
||||
v, err = parse(s)
|
||||
if err == nil {
|
||||
w = 1
|
||||
}
|
||||
return v, w, err
|
||||
}
|
||||
|
||||
// Round returns the half away from zero rounded value of x with prec precision.
|
||||
//
|
||||
// Special cases are:
|
||||
// Round(±0) = +0
|
||||
// Round(±Inf) = ±Inf
|
||||
// Round(NaN) = NaN
|
||||
func Round(x complex128, prec int) complex128 {
|
||||
if x == 0 {
|
||||
// Make sure zero is returned
|
||||
// without the negative bit set.
|
||||
return 0
|
||||
}
|
||||
return complex(scalar.Round(real(x), prec), scalar.Round(imag(x), prec))
|
||||
}
|
||||
|
||||
// RoundEven returns the half even rounded value of x with prec precision.
|
||||
//
|
||||
// Special cases are:
|
||||
// RoundEven(±0) = +0
|
||||
// RoundEven(±Inf) = ±Inf
|
||||
// RoundEven(NaN) = NaN
|
||||
func RoundEven(x complex128, prec int) complex128 {
|
||||
if x == 0 {
|
||||
// Make sure zero is returned
|
||||
// without the negative bit set.
|
||||
return 0
|
||||
}
|
||||
return complex(scalar.RoundEven(real(x), prec), scalar.RoundEven(imag(x), prec))
|
||||
}
|
||||
|
||||
// Same returns true if the inputs have the same value with NaN treated as the same.
|
||||
func Same(a, b complex128) bool {
|
||||
return a == b || (cmplx.IsNaN(a) && cmplx.IsNaN(b))
|
||||
}
|
||||
198
cmplxs/cscalar/cscalar_test.go
Normal file
198
cmplxs/cscalar/cscalar_test.go
Normal file
@@ -0,0 +1,198 @@
|
||||
// Copyright ©2013 The Gonum Authors. All rights reserved.
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file
|
||||
|
||||
package cscalar
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/cmplx"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEqualsRelative(t *testing.T) {
|
||||
equalityTests := []struct {
|
||||
a, b float64
|
||||
tol float64
|
||||
equal bool
|
||||
}{
|
||||
{1000000, 1000001, 0, true},
|
||||
{1000001, 1000000, 0, true},
|
||||
{10000, 10001, 0, false},
|
||||
{10001, 10000, 0, false},
|
||||
{-1000000, -1000001, 0, true},
|
||||
{-1000001, -1000000, 0, true},
|
||||
{-10000, -10001, 0, false},
|
||||
{-10001, -10000, 0, false},
|
||||
{1.0000001, 1.0000002, 0, true},
|
||||
{1.0000002, 1.0000001, 0, true},
|
||||
{1.0002, 1.0001, 0, false},
|
||||
{1.0001, 1.0002, 0, false},
|
||||
{-1.000001, -1.000002, 0, true},
|
||||
{-1.000002, -1.000001, 0, true},
|
||||
{-1.0001, -1.0002, 0, false},
|
||||
{-1.0002, -1.0001, 0, false},
|
||||
{0.000000001000001, 0.000000001000002, 0, true},
|
||||
{0.000000001000002, 0.000000001000001, 0, true},
|
||||
{0.000000000001002, 0.000000000001001, 0, false},
|
||||
{0.000000000001001, 0.000000000001002, 0, false},
|
||||
{-0.000000001000001, -0.000000001000002, 0, true},
|
||||
{-0.000000001000002, -0.000000001000001, 0, true},
|
||||
{-0.000000000001002, -0.000000000001001, 0, false},
|
||||
{-0.000000000001001, -0.000000000001002, 0, false},
|
||||
{0, 0, 0, true},
|
||||
{0, -0, 0, true},
|
||||
{-0, -0, 0, true},
|
||||
{0.00000001, 0, 0, false},
|
||||
{0, 0.00000001, 0, false},
|
||||
{-0.00000001, 0, 0, false},
|
||||
{0, -0.00000001, 0, false},
|
||||
{0, 1e-310, 0.01, true},
|
||||
{1e-310, 0, 0.01, true},
|
||||
{1e-310, 0, 0.000001, false},
|
||||
{0, 1e-310, 0.000001, false},
|
||||
{0, -1e-310, 0.1, true},
|
||||
{-1e-310, 0, 0.1, true},
|
||||
{-1e-310, 0, 0.00000001, false},
|
||||
{0, -1e-310, 0.00000001, false},
|
||||
{math.Inf(1), math.Inf(1), 0, true},
|
||||
{math.Inf(1), math.MaxFloat64, 0, false},
|
||||
{math.NaN(), math.NaN(), 0, false},
|
||||
{math.NaN(), 0, 0, false},
|
||||
{-0, math.NaN(), 0, false},
|
||||
{math.NaN(), -0, 0, false},
|
||||
{0, math.NaN(), 0, false},
|
||||
{math.NaN(), math.Inf(1), 0, false},
|
||||
{math.Inf(1), math.NaN(), 0, false},
|
||||
{math.NaN(), math.MaxFloat64, 0, false},
|
||||
{math.MaxFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), -math.MaxFloat64, 0, false},
|
||||
{-math.MaxFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, math.NaN(), 0, false},
|
||||
{math.NaN(), -math.SmallestNonzeroFloat64, 0, false},
|
||||
{-math.SmallestNonzeroFloat64, math.NaN(), 0, false},
|
||||
{1.000000001, -1.0, 0, false},
|
||||
{-1.0, 1.000000001, 0, false},
|
||||
{-1.000000001, 1.0, 0, false},
|
||||
{1.0, -1.000000001, 0, false},
|
||||
{10 * math.SmallestNonzeroFloat64, 10 * -math.SmallestNonzeroFloat64, 0, true},
|
||||
{1e11 * math.SmallestNonzeroFloat64, 1e11 * -math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, 0, true},
|
||||
{-math.SmallestNonzeroFloat64, math.SmallestNonzeroFloat64, 0, true},
|
||||
{math.SmallestNonzeroFloat64, 0, 0, true},
|
||||
{0, math.SmallestNonzeroFloat64, 0, true},
|
||||
{-math.SmallestNonzeroFloat64, 0, 0, true},
|
||||
{0, -math.SmallestNonzeroFloat64, 0, true},
|
||||
{0.000000001, -math.SmallestNonzeroFloat64, 0, false},
|
||||
{0.000000001, math.SmallestNonzeroFloat64, 0, false},
|
||||
{math.SmallestNonzeroFloat64, 0.000000001, 0, false},
|
||||
{-math.SmallestNonzeroFloat64, 0.000000001, 0, false},
|
||||
}
|
||||
for _, ts := range equalityTests {
|
||||
if ts.tol == 0 {
|
||||
ts.tol = 1e-5
|
||||
}
|
||||
|
||||
for _, comp := range []struct{ a, b complex128 }{
|
||||
{a: complex(ts.a, 0), b: complex(ts.b, 0)},
|
||||
{a: complex(0, ts.a), b: complex(0, ts.b)},
|
||||
{a: complex(ts.a, ts.a), b: complex(ts.b, ts.b)},
|
||||
} {
|
||||
if equal := EqualWithinRel(comp.a, comp.b, ts.tol); equal != ts.equal {
|
||||
t.Errorf("Relative equality of %g and %g with tolerance %g returned: %v. Expected: %v",
|
||||
comp.a, comp.b, ts.tol, equal, ts.equal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundEven(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
x complex128
|
||||
prec int
|
||||
want complex128
|
||||
}{
|
||||
{x: 0, prec: 1, want: 0},
|
||||
{x: cmplx.Inf(), prec: 1, want: cmplx.Inf()},
|
||||
{x: cmplx.NaN(), prec: 1, want: cmplx.NaN()},
|
||||
{x: func() complex128 { var f complex128; return -f }(), prec: 1, want: 0},
|
||||
{x: math.MaxFloat64 / 2, prec: 1, want: math.MaxFloat64 / 2},
|
||||
{x: 1 << 64, prec: 1, want: 1 << 64},
|
||||
{x: 454.4445, prec: 3, want: 454.444},
|
||||
{x: 454.44445, prec: 4, want: 454.4444},
|
||||
{x: 0.42499, prec: 4, want: 0.425},
|
||||
{x: 0.42599, prec: 4, want: 0.426},
|
||||
{x: 0.424999999999993, prec: 2, want: 0.42},
|
||||
{x: 0.425, prec: 2, want: 0.42},
|
||||
{x: 0.425000000000001, prec: 2, want: 0.43},
|
||||
{x: 123.4244999999999, prec: 3, want: 123.424},
|
||||
{x: 123.4245, prec: 3, want: 123.424},
|
||||
{x: 123.4245000000001, prec: 3, want: 123.425},
|
||||
|
||||
{x: 454.45, prec: 0, want: 454},
|
||||
{x: 454.45, prec: 1, want: 454.4},
|
||||
{x: 454.45, prec: 2, want: 454.45},
|
||||
{x: 454.45, prec: 3, want: 454.45},
|
||||
{x: 454.445, prec: 0, want: 454},
|
||||
{x: 454.445, prec: 1, want: 454.4},
|
||||
{x: 454.445, prec: 2, want: 454.44},
|
||||
{x: 454.445, prec: 3, want: 454.445},
|
||||
{x: 454.445, prec: 4, want: 454.445},
|
||||
{x: 454.55, prec: 0, want: 455},
|
||||
{x: 454.55, prec: 1, want: 454.6},
|
||||
{x: 454.55, prec: 2, want: 454.55},
|
||||
{x: 454.55, prec: 3, want: 454.55},
|
||||
{x: 454.455, prec: 0, want: 454},
|
||||
{x: 454.455, prec: 1, want: 454.5},
|
||||
{x: 454.455, prec: 2, want: 454.46},
|
||||
{x: 454.455, prec: 3, want: 454.455},
|
||||
{x: 454.455, prec: 4, want: 454.455},
|
||||
|
||||
// Negative precision.
|
||||
{x: 454.45, prec: -1, want: 450},
|
||||
{x: 454.45, prec: -2, want: 500},
|
||||
{x: 500, prec: -3, want: 0},
|
||||
{x: 500, prec: -4, want: 0},
|
||||
{x: 1500, prec: -3, want: 2000},
|
||||
{x: 1500, prec: -4, want: 0},
|
||||
} {
|
||||
for _, sign := range []complex128{1, -1} {
|
||||
got := RoundEven(sign*test.x, test.prec)
|
||||
want := sign * test.want
|
||||
if want == 0 {
|
||||
want = 0
|
||||
}
|
||||
// FIXME(kortschak): Complexify this.
|
||||
if (got != want || math.Signbit(real(got)) != math.Signbit(real(want))) && !(math.IsNaN(real(got)) && math.IsNaN(real(want))) {
|
||||
t.Errorf("unexpected result for RoundEven(%g, %d): got: %g, want: %g", sign*test.x, test.prec, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSame(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, test := range []struct {
|
||||
a, b complex128
|
||||
want bool
|
||||
}{
|
||||
{a: 0, b: 0, want: true},
|
||||
{a: 1, b: 1, want: true},
|
||||
{a: -1, b: 1, want: false},
|
||||
{a: 0, b: 1, want: false},
|
||||
{a: 1, b: 0, want: false},
|
||||
{a: -1, b: 1, want: false},
|
||||
{a: cmplx.NaN(), b: cmplx.NaN(), want: true},
|
||||
{a: 1, b: cmplx.NaN(), want: false},
|
||||
{a: cmplx.Inf(), b: cmplx.NaN(), want: false},
|
||||
{a: cmplx.NaN(), b: cmplx.Inf(), want: false},
|
||||
{a: cmplx.NaN(), b: 1, want: false},
|
||||
{a: cmplx.Inf(), b: cmplx.Inf(), want: true},
|
||||
} {
|
||||
got := Same(test.a, test.b)
|
||||
if got != test.want {
|
||||
t.Errorf("unexpected results for a=%f b=%f: got:%t want:%t", test.a, test.b, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
6
cmplxs/cscalar/doc.go
Normal file
6
cmplxs/cscalar/doc.go
Normal file
@@ -0,0 +1,6 @@
|
||||
// Copyright ©2020 The Gonum 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 cscalar provides a set of helper routines for dealing with complex128 values.
|
||||
package cscalar // import "gonum.org/v1/gonum/cmplxs/cscalar"
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file
|
||||
|
||||
package cmplxs
|
||||
package cscalar
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file
|
||||
|
||||
package cmplxs_test
|
||||
package cscalar_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"gonum.org/v1/gonum/cmplxs"
|
||||
"gonum.org/v1/gonum/cmplxs/cscalar"
|
||||
"gonum.org/v1/gonum/floats"
|
||||
)
|
||||
|
||||
@@ -28,7 +29,7 @@ missing
|
||||
)
|
||||
sc := bufio.NewScanner(strings.NewReader(data))
|
||||
for sc.Scan() {
|
||||
v, w, err := cmplxs.ParseWithNA(sc.Text(), "missing")
|
||||
v, w, err := cscalar.ParseWithNA(sc.Text(), "missing")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file
|
||||
|
||||
package cmplxs
|
||||
package cscalar
|
||||
|
||||
import (
|
||||
"math"
|
||||
@@ -85,12 +85,8 @@ func TestParse(t *testing.T) {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !same(got, test.want) {
|
||||
if !Same(got, test.want) {
|
||||
t.Errorf("unexpected result for Parse(%q): got:%v, want:%v", test.s, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func same(a, b complex128) bool {
|
||||
return a == b || (cmplx.IsNaN(a) && cmplx.IsNaN(b))
|
||||
}
|
||||
Reference in New Issue
Block a user