gonum.org/v1/gonum@v0.14.0/num/hyperdual/hyperdual.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 hyperdual 6 7 import ( 8 "fmt" 9 "math" 10 "strings" 11 ) 12 13 // Number is a float64 precision hyperdual number. 14 type Number struct { 15 Real, E1mag, E2mag, E1E2mag float64 16 } 17 18 var negZero = math.Float64frombits(1 << 63) 19 20 // Format implements fmt.Formatter. 21 func (d Number) Format(fs fmt.State, c rune) { 22 prec, pOk := fs.Precision() 23 if !pOk { 24 prec = -1 25 } 26 width, wOk := fs.Width() 27 if !wOk { 28 width = -1 29 } 30 switch c { 31 case 'v': 32 if fs.Flag('#') { 33 fmt.Fprintf(fs, "%T{Real:%#v, E1mag:%#v, E2mag:%#v, E1E2mag:%#v}", d, d.Real, d.E1mag, d.E2mag, d.E1E2mag) 34 return 35 } 36 if fs.Flag('+') { 37 fmt.Fprintf(fs, "{Real:%+v, E1mag:%+v, E2mag:%+v, E1E2mag:%+v}", d.Real, d.E1mag, d.E2mag, d.E1E2mag) 38 return 39 } 40 c = 'g' 41 prec = -1 42 fallthrough 43 case 'e', 'E', 'f', 'F', 'g', 'G': 44 fre := fmtString(fs, c, prec, width, false) 45 fim := fmtString(fs, c, prec, width, true) 46 fmt.Fprintf(fs, fmt.Sprintf("(%s%[2]sϵ₁%[2]sϵ₂%[2]sϵ₁ϵ₂)", fre, fim), d.Real, d.E1mag, d.E2mag, d.E1E2mag) 47 default: 48 fmt.Fprintf(fs, "%%!%c(%T=%[2]v)", c, d) 49 return 50 } 51 } 52 53 // This is horrible, but it's what we have. 54 func fmtString(fs fmt.State, c rune, prec, width int, wantPlus bool) string { 55 var b strings.Builder 56 b.WriteByte('%') 57 for _, f := range "0+- " { 58 if fs.Flag(int(f)) || (f == '+' && wantPlus) { 59 b.WriteByte(byte(f)) 60 } 61 } 62 if width >= 0 { 63 fmt.Fprint(&b, width) 64 } 65 if prec >= 0 { 66 b.WriteByte('.') 67 if prec > 0 { 68 fmt.Fprint(&b, prec) 69 } 70 } 71 b.WriteRune(c) 72 return b.String() 73 } 74 75 // Add returns the sum of x and y. 76 func Add(x, y Number) Number { 77 return Number{ 78 Real: x.Real + y.Real, 79 E1mag: x.E1mag + y.E1mag, 80 E2mag: x.E2mag + y.E2mag, 81 E1E2mag: x.E1E2mag + y.E1E2mag, 82 } 83 } 84 85 // Sub returns the difference of x and y, x-y. 86 func Sub(x, y Number) Number { 87 return Number{ 88 Real: x.Real - y.Real, 89 E1mag: x.E1mag - y.E1mag, 90 E2mag: x.E2mag - y.E2mag, 91 E1E2mag: x.E1E2mag - y.E1E2mag, 92 } 93 } 94 95 // Mul returns the hyperdual product of x and y. 96 func Mul(x, y Number) Number { 97 return Number{ 98 Real: x.Real * y.Real, 99 E1mag: x.Real*y.E1mag + x.E1mag*y.Real, 100 E2mag: x.Real*y.E2mag + x.E2mag*y.Real, 101 E1E2mag: x.Real*y.E1E2mag + x.E1mag*y.E2mag + x.E2mag*y.E1mag + x.E1E2mag*y.Real, 102 } 103 } 104 105 // Inv returns the hyperdual inverse of d. 106 // 107 // Special cases are: 108 // 109 // Inv(±Inf) = ±0-0ϵ₁-0ϵ₂±0ϵ₁ϵ₂ 110 // Inv(±0) = ±Inf-Infϵ₁-Infϵ₂±Infϵ₁ϵ₂ 111 func Inv(d Number) Number { 112 if d.Real == 0 { 113 return Number{ 114 Real: 1 / d.Real, 115 E1mag: math.Inf(-1), 116 E2mag: math.Inf(-1), 117 E1E2mag: 1 / d.Real, // Return a signed inf from a signed zero. 118 } 119 } 120 d2 := d.Real * d.Real 121 return Number{ 122 Real: 1 / d.Real, 123 E1mag: -d.E1mag / d2, 124 E2mag: -d.E2mag / d2, 125 E1E2mag: -d.E1E2mag/d2 + 2*d.E1mag*d.E2mag/(d2*d.Real), 126 } 127 } 128 129 // Scale returns d scaled by f. 130 func Scale(f float64, d Number) Number { 131 return Number{Real: f * d.Real, E1mag: f * d.E1mag, E2mag: f * d.E2mag, E1E2mag: f * d.E1E2mag} 132 } 133 134 // Abs returns the absolute value of d. 135 func Abs(d Number) Number { 136 if math.Float64bits(d.Real)&(1<<63) == 0 { 137 return d 138 } 139 return Scale(-1, d) 140 }