gonum.org/v1/gonum@v0.14.0/num/dual/dual_test.go (about)

     1  // Copyright ©2018 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package dual
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"testing"
    11  
    12  	"gonum.org/v1/gonum/floats/scalar"
    13  )
    14  
    15  var formatTests = []struct {
    16  	d      Number
    17  	format string
    18  	want   string
    19  }{
    20  	{d: Number{1.1, 2.1}, format: "%#v", want: "dual.Number{Real:1.1, Emag:2.1}"},     // Bootstrap test.
    21  	{d: Number{-1.1, -2.1}, format: "%#v", want: "dual.Number{Real:-1.1, Emag:-2.1}"}, // Bootstrap test.
    22  	{d: Number{1.1, 2.1}, format: "%+v", want: "{Real:1.1, Emag:2.1}"},
    23  	{d: Number{-1.1, -2.1}, format: "%+v", want: "{Real:-1.1, Emag:-2.1}"},
    24  	{d: Number{1, 2}, format: "%v", want: "(1+2ϵ)"},
    25  	{d: Number{-1, -2}, format: "%v", want: "(-1-2ϵ)"},
    26  	{d: Number{1, 2}, format: "%g", want: "(1+2ϵ)"},
    27  	{d: Number{-1, -2}, format: "%g", want: "(-1-2ϵ)"},
    28  	{d: Number{1, 2}, format: "%e", want: "(1.000000e+00+2.000000e+00ϵ)"},
    29  	{d: Number{-1, -2}, format: "%e", want: "(-1.000000e+00-2.000000e+00ϵ)"},
    30  	{d: Number{1, 2}, format: "%E", want: "(1.000000E+00+2.000000E+00ϵ)"},
    31  	{d: Number{-1, -2}, format: "%E", want: "(-1.000000E+00-2.000000E+00ϵ)"},
    32  	{d: Number{1, 2}, format: "%f", want: "(1.000000+2.000000ϵ)"},
    33  	{d: Number{-1, -2}, format: "%f", want: "(-1.000000-2.000000ϵ)"},
    34  }
    35  
    36  func TestFormat(t *testing.T) {
    37  	t.Parallel()
    38  	for _, test := range formatTests {
    39  		got := fmt.Sprintf(test.format, test.d)
    40  		if got != test.want {
    41  			t.Errorf("unexpected result for fmt.Sprintf(%q, %#v): got:%q, want:%q", test.format, test.d, got, test.want)
    42  		}
    43  	}
    44  }
    45  
    46  // First derivatives:
    47  
    48  func dSin(x float64) float64  { return math.Cos(x) }
    49  func dCos(x float64) float64  { return -math.Sin(x) }
    50  func dTan(x float64) float64  { return sec(x) * sec(x) }
    51  func dAsin(x float64) float64 { return 1 / math.Sqrt(1-x*x) }
    52  func dAcos(x float64) float64 { return -1 / math.Sqrt(1-x*x) }
    53  func dAtan(x float64) float64 { return 1 / (1 + x*x) }
    54  
    55  func dSinh(x float64) float64  { return math.Cosh(x) }
    56  func dCosh(x float64) float64  { return math.Sinh(x) }
    57  func dTanh(x float64) float64  { return sech(x) * sech(x) }
    58  func dAsinh(x float64) float64 { return 1 / math.Sqrt(x*x+1) }
    59  func dAcosh(x float64) float64 { return 1 / (math.Sqrt(x-1) * math.Sqrt(x+1)) }
    60  func dAtanh(x float64) float64 {
    61  	switch {
    62  	case math.Abs(x) == 1:
    63  		return math.NaN()
    64  	case math.IsInf(x, 0):
    65  		return negZero
    66  	}
    67  	return 1 / (1 - x*x)
    68  }
    69  
    70  func dExp(x float64) float64 { return math.Exp(x) }
    71  func dLog(x float64) float64 {
    72  	if x < 0 {
    73  		return math.NaN()
    74  	}
    75  	return 1 / x
    76  }
    77  func dSqrt(x float64) float64 {
    78  	// For whatever reason, math.Sqrt(-0) returns -0.
    79  	// In this case, that is clearly a wrong approach.
    80  	if x == 0 {
    81  		return math.Inf(1)
    82  	}
    83  	return 0.5 / math.Sqrt(x)
    84  }
    85  func dInv(x float64) float64 { return -1 / (x * x) }
    86  
    87  // Helpers:
    88  
    89  func sec(x float64) float64  { return 1 / math.Cos(x) }
    90  func sech(x float64) float64 { return 1 / math.Cosh(x) }
    91  
    92  var negZero = math.Float64frombits(1 << 63)
    93  
    94  var dualTests = []struct {
    95  	name   string
    96  	x      []float64
    97  	fnDual func(x Number) Number
    98  	fn     func(x float64) float64
    99  	dFn    func(x float64) float64
   100  }{
   101  	{
   102  		name:   "sin",
   103  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   104  		fnDual: Sin,
   105  		fn:     math.Sin,
   106  		dFn:    dSin,
   107  	},
   108  	{
   109  		name:   "cos",
   110  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   111  		fnDual: Cos,
   112  		fn:     math.Cos,
   113  		dFn:    dCos,
   114  	},
   115  	{
   116  		name:   "tan",
   117  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   118  		fnDual: Tan,
   119  		fn:     math.Tan,
   120  		dFn:    dTan,
   121  	},
   122  	{
   123  		name:   "sinh",
   124  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   125  		fnDual: Sinh,
   126  		fn:     math.Sinh,
   127  		dFn:    dSinh,
   128  	},
   129  	{
   130  		name:   "cosh",
   131  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   132  		fnDual: Cosh,
   133  		fn:     math.Cosh,
   134  		dFn:    dCosh,
   135  	},
   136  	{
   137  		name:   "tanh",
   138  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   139  		fnDual: Tanh,
   140  		fn:     math.Tanh,
   141  		dFn:    dTanh,
   142  	},
   143  
   144  	{
   145  		name:   "asin",
   146  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   147  		fnDual: Asin,
   148  		fn:     math.Asin,
   149  		dFn:    dAsin,
   150  	},
   151  	{
   152  		name:   "acos",
   153  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   154  		fnDual: Acos,
   155  		fn:     math.Acos,
   156  		dFn:    dAcos,
   157  	},
   158  	{
   159  		name:   "atan",
   160  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   161  		fnDual: Atan,
   162  		fn:     math.Atan,
   163  		dFn:    dAtan,
   164  	},
   165  	{
   166  		name:   "asinh",
   167  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   168  		fnDual: Asinh,
   169  		fn:     math.Asinh,
   170  		dFn:    dAsinh,
   171  	},
   172  	{
   173  		name:   "acosh",
   174  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   175  		fnDual: Acosh,
   176  		fn:     math.Acosh,
   177  		dFn:    dAcosh,
   178  	},
   179  	{
   180  		name:   "atanh",
   181  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   182  		fnDual: Atanh,
   183  		fn:     math.Atanh,
   184  		dFn:    dAtanh,
   185  	},
   186  
   187  	{
   188  		name:   "exp",
   189  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   190  		fnDual: Exp,
   191  		fn:     math.Exp,
   192  		dFn:    dExp,
   193  	},
   194  	{
   195  		name:   "log",
   196  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   197  		fnDual: Log,
   198  		fn:     math.Log,
   199  		dFn:    dLog,
   200  	},
   201  	{
   202  		name:   "inv",
   203  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   204  		fnDual: Inv,
   205  		fn:     func(x float64) float64 { return 1 / x },
   206  		dFn:    dInv,
   207  	},
   208  	{
   209  		name:   "sqrt",
   210  		x:      []float64{math.NaN(), math.Inf(-1), -3, -2, -1, -0.5, negZero, 0, 0.5, 1, 2, 3, math.Inf(1)},
   211  		fnDual: Sqrt,
   212  		fn:     math.Sqrt,
   213  		dFn:    dSqrt,
   214  	},
   215  
   216  	{
   217  		name: "Fike example fn",
   218  		x:    []float64{1, 2, 3, 4, 5},
   219  		fnDual: func(x Number) Number {
   220  			return Mul(
   221  				Exp(x),
   222  				Inv(Sqrt(
   223  					Add(
   224  						PowReal(Sin(x), 3),
   225  						PowReal(Cos(x), 3)))))
   226  		},
   227  		fn: func(x float64) float64 {
   228  			return math.Exp(x) / math.Sqrt(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3))
   229  		},
   230  		dFn: func(x float64) float64 {
   231  			return math.Exp(x) * (3*math.Cos(x) + 5*math.Cos(3*x) + 9*math.Sin(x) + math.Sin(3*x)) /
   232  				(8 * math.Pow(math.Pow(math.Sin(x), 3)+math.Pow(math.Cos(x), 3), 1.5))
   233  		},
   234  	},
   235  }
   236  
   237  func TestDual(t *testing.T) {
   238  	t.Parallel()
   239  	const tol = 1e-15
   240  	for _, test := range dualTests {
   241  		for _, x := range test.x {
   242  			fxDual := test.fnDual(Number{Real: x, Emag: 1})
   243  			fx := test.fn(x)
   244  			dFx := test.dFn(x)
   245  			if !same(fxDual.Real, fx, tol) {
   246  				t.Errorf("unexpected %s(%v): got:%v want:%v", test.name, x, fxDual.Real, fx)
   247  			}
   248  			if !same(fxDual.Emag, dFx, tol) {
   249  				t.Errorf("unexpected %s'(%v): got:%v want:%v", test.name, x, fxDual.Emag, dFx)
   250  			}
   251  		}
   252  	}
   253  }
   254  
   255  var powRealTests = []struct {
   256  	d    Number
   257  	p    float64
   258  	want Number
   259  }{
   260  	// PowReal(NaN+xϵ, ±0) = 1+NaNϵ for any x
   261  	{d: Number{Real: math.NaN(), Emag: 0}, p: 0, want: Number{Real: 1, Emag: math.NaN()}},
   262  	{d: Number{Real: math.NaN(), Emag: 0}, p: negZero, want: Number{Real: 1, Emag: math.NaN()}},
   263  	{d: Number{Real: math.NaN(), Emag: 1}, p: 0, want: Number{Real: 1, Emag: math.NaN()}},
   264  	{d: Number{Real: math.NaN(), Emag: 2}, p: negZero, want: Number{Real: 1, Emag: math.NaN()}},
   265  	{d: Number{Real: math.NaN(), Emag: 3}, p: 0, want: Number{Real: 1, Emag: math.NaN()}},
   266  	{d: Number{Real: math.NaN(), Emag: 1}, p: negZero, want: Number{Real: 1, Emag: math.NaN()}},
   267  	{d: Number{Real: math.NaN(), Emag: 2}, p: 0, want: Number{Real: 1, Emag: math.NaN()}},
   268  	{d: Number{Real: math.NaN(), Emag: 3}, p: negZero, want: Number{Real: 1, Emag: math.NaN()}},
   269  
   270  	// PowReal(x, ±0) = 1 for any x
   271  	{d: Number{Real: 0, Emag: 0}, p: 0, want: Number{Real: 1, Emag: 0}},
   272  	{d: Number{Real: negZero, Emag: 0}, p: negZero, want: Number{Real: 1, Emag: 0}},
   273  	{d: Number{Real: math.Inf(1), Emag: 0}, p: 0, want: Number{Real: 1, Emag: 0}},
   274  	{d: Number{Real: math.Inf(-1), Emag: 0}, p: negZero, want: Number{Real: 1, Emag: 0}},
   275  	{d: Number{Real: 0, Emag: 1}, p: 0, want: Number{Real: 1, Emag: 0}},
   276  	{d: Number{Real: negZero, Emag: 1}, p: negZero, want: Number{Real: 1, Emag: 0}},
   277  	{d: Number{Real: math.Inf(1), Emag: 1}, p: 0, want: Number{Real: 1, Emag: 0}},
   278  	{d: Number{Real: math.Inf(-1), Emag: 1}, p: negZero, want: Number{Real: 1, Emag: 0}},
   279  
   280  	// PowReal(1+xϵ, y) = (1+xyϵ) for any y
   281  	{d: Number{Real: 1, Emag: 0}, p: 0, want: Number{Real: 1, Emag: 0}},
   282  	{d: Number{Real: 1, Emag: 0}, p: 1, want: Number{Real: 1, Emag: 0}},
   283  	{d: Number{Real: 1, Emag: 0}, p: 2, want: Number{Real: 1, Emag: 0}},
   284  	{d: Number{Real: 1, Emag: 0}, p: 3, want: Number{Real: 1, Emag: 0}},
   285  	{d: Number{Real: 1, Emag: 1}, p: 0, want: Number{Real: 1, Emag: 0}},
   286  	{d: Number{Real: 1, Emag: 1}, p: 1, want: Number{Real: 1, Emag: 1}},
   287  	{d: Number{Real: 1, Emag: 1}, p: 2, want: Number{Real: 1, Emag: 2}},
   288  	{d: Number{Real: 1, Emag: 1}, p: 3, want: Number{Real: 1, Emag: 3}},
   289  	{d: Number{Real: 1, Emag: 2}, p: 0, want: Number{Real: 1, Emag: 0}},
   290  	{d: Number{Real: 1, Emag: 2}, p: 1, want: Number{Real: 1, Emag: 2}},
   291  	{d: Number{Real: 1, Emag: 2}, p: 2, want: Number{Real: 1, Emag: 4}},
   292  	{d: Number{Real: 1, Emag: 2}, p: 3, want: Number{Real: 1, Emag: 6}},
   293  
   294  	// PowReal(x, 1) = x for any x
   295  	{d: Number{Real: 0, Emag: 0}, p: 1, want: Number{Real: 0, Emag: 0}},
   296  	{d: Number{Real: negZero, Emag: 0}, p: 1, want: Number{Real: negZero, Emag: 0}},
   297  	{d: Number{Real: 0, Emag: 1}, p: 1, want: Number{Real: 0, Emag: 1}},
   298  	{d: Number{Real: negZero, Emag: 1}, p: 1, want: Number{Real: negZero, Emag: 1}},
   299  	{d: Number{Real: math.NaN(), Emag: 0}, p: 1, want: Number{Real: math.NaN(), Emag: 0}},
   300  	{d: Number{Real: math.NaN(), Emag: 1}, p: 1, want: Number{Real: math.NaN(), Emag: 1}},
   301  	{d: Number{Real: math.NaN(), Emag: 2}, p: 1, want: Number{Real: math.NaN(), Emag: 2}},
   302  
   303  	// PowReal(NaN+xϵ, y) = NaN+NaNϵ
   304  	{d: Number{Real: math.NaN(), Emag: 0}, p: 2, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   305  	{d: Number{Real: math.NaN(), Emag: 0}, p: 3, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   306  	{d: Number{Real: math.NaN(), Emag: 1}, p: 2, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   307  	{d: Number{Real: math.NaN(), Emag: 1}, p: 3, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   308  	{d: Number{Real: math.NaN(), Emag: 2}, p: 2, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   309  	{d: Number{Real: math.NaN(), Emag: 2}, p: 3, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   310  
   311  	// PowReal(x, NaN) = NaN+NaNϵ
   312  	{d: Number{Real: 0, Emag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   313  	{d: Number{Real: 2, Emag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   314  	{d: Number{Real: 3, Emag: 0}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   315  	{d: Number{Real: 0, Emag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   316  	{d: Number{Real: 2, Emag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   317  	{d: Number{Real: 3, Emag: 1}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   318  	{d: Number{Real: 0, Emag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   319  	{d: Number{Real: 2, Emag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   320  	{d: Number{Real: 3, Emag: 2}, p: math.NaN(), want: Number{Real: math.NaN(), Emag: math.NaN()}},
   321  
   322  	// Handled by math.Pow tests:
   323  	//
   324  	// Pow(±0, y) = ±Inf for y an odd integer < 0
   325  	// Pow(±0, -Inf) = +Inf
   326  	// Pow(±0, +Inf) = +0
   327  	// Pow(±0, y) = +Inf for finite y < 0 and not an odd integer
   328  	// Pow(±0, y) = ±0 for y an odd integer > 0
   329  	// Pow(±0, y) = +0 for finite y > 0 and not an odd integer
   330  	// Pow(-1, ±Inf) = 1
   331  
   332  	// PowReal(x+0ϵ, +Inf) = +Inf+NaNϵ for |x| > 1
   333  	{d: Number{Real: 2, Emag: 0}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.NaN()}},
   334  	{d: Number{Real: 3, Emag: 0}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.NaN()}},
   335  
   336  	// PowReal(x+yϵ, +Inf) = +Inf for |x| > 1
   337  	{d: Number{Real: 2, Emag: 1}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.Inf(1)}},
   338  	{d: Number{Real: 3, Emag: 1}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.Inf(1)}},
   339  	{d: Number{Real: 2, Emag: 2}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.Inf(1)}},
   340  	{d: Number{Real: 3, Emag: 2}, p: math.Inf(1), want: Number{Real: math.Inf(1), Emag: math.Inf(1)}},
   341  
   342  	// PowReal(x, -Inf) = +0+NaNϵ for |x| > 1
   343  	{d: Number{Real: 2, Emag: 0}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   344  	{d: Number{Real: 3, Emag: 0}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   345  	{d: Number{Real: 2, Emag: 1}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   346  	{d: Number{Real: 3, Emag: 1}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   347  	{d: Number{Real: 2, Emag: 2}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   348  	{d: Number{Real: 3, Emag: 2}, p: math.Inf(-1), want: Number{Real: 0, Emag: math.NaN()}},
   349  
   350  	// PowReal(x+yϵ, +Inf) = +0+NaNϵ for |x| < 1
   351  	{d: Number{Real: 0.1, Emag: 0}, p: math.Inf(1), want: Number{Real: 0, Emag: math.NaN()}},
   352  	{d: Number{Real: 0.1, Emag: 0.1}, p: math.Inf(1), want: Number{Real: 0, Emag: math.NaN()}},
   353  	{d: Number{Real: 0.2, Emag: 0.2}, p: math.Inf(1), want: Number{Real: 0, Emag: math.NaN()}},
   354  	{d: Number{Real: 0.5, Emag: 0.5}, p: math.Inf(1), want: Number{Real: 0, Emag: math.NaN()}},
   355  
   356  	// PowReal(x+0ϵ, -Inf) = +Inf+NaNϵ for |x| < 1
   357  	{d: Number{Real: 0.1, Emag: 0}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.NaN()}},
   358  	{d: Number{Real: 0.2, Emag: 0}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.NaN()}},
   359  
   360  	// PowReal(x, -Inf) = +Inf-Infϵ for |x| < 1
   361  	{d: Number{Real: 0.1, Emag: 0.1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   362  	{d: Number{Real: 0.2, Emag: 0.1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   363  	{d: Number{Real: 0.1, Emag: 0.2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   364  	{d: Number{Real: 0.2, Emag: 0.2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   365  	{d: Number{Real: 0.1, Emag: 1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   366  	{d: Number{Real: 0.2, Emag: 1}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   367  	{d: Number{Real: 0.1, Emag: 2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   368  	{d: Number{Real: 0.2, Emag: 2}, p: math.Inf(-1), want: Number{Real: math.Inf(1), Emag: math.Inf(-1)}},
   369  
   370  	// Handled by math.Pow tests:
   371  	//
   372  	// Pow(+Inf, y) = +Inf for y > 0
   373  	// Pow(+Inf, y) = +0 for y < 0
   374  	// Pow(-Inf, y) = Pow(-0, -y)
   375  
   376  	// PowReal(x, y) = NaN+NaNϵ for finite x < 0 and finite non-integer y
   377  	{d: Number{Real: -1, Emag: -1}, p: 0.5, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   378  	{d: Number{Real: -1, Emag: 2}, p: 0.5, want: Number{Real: math.NaN(), Emag: math.NaN()}},
   379  }
   380  
   381  func TestPowReal(t *testing.T) {
   382  	t.Parallel()
   383  	const tol = 1e-15
   384  	for _, test := range powRealTests {
   385  		got := PowReal(test.d, test.p)
   386  		if !sameDual(got, test.want, tol) {
   387  			t.Errorf("unexpected PowReal(%v, %v): got:%v want:%v", test.d, test.p, got, test.want)
   388  		}
   389  	}
   390  }
   391  
   392  func sameDual(a, b Number, tol float64) bool {
   393  	return same(a.Real, b.Real, tol) && same(a.Emag, b.Emag, tol)
   394  }
   395  
   396  func same(a, b, tol float64) bool {
   397  	return (math.IsNaN(a) && math.IsNaN(b)) || scalar.EqualWithinAbsOrRel(a, b, tol, tol)
   398  }