1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
package helpers
import "math"
// This wraps float64 math operations. Why does this exist? The Go compiler
// contains some optimizations to take advantage of "fused multiply and add"
// (FMA) instructions on certain processors. These instructions lead to
// different output on those processors, which means esbuild's output is no
// longer deterministic across all platforms. From the Go specification itself
// (https://go.dev/ref/spec#Floating_point_operators):
//
// An implementation may combine multiple floating-point operations into a
// single fused operation, possibly across statements, and produce a result
// that differs from the value obtained by executing and rounding the
// instructions individually. An explicit floating-point type conversion
// rounds to the precision of the target type, preventing fusion that would
// discard that rounding.
//
// For instance, some architectures provide a "fused multiply and add" (FMA)
// instruction that computes x*y + z without rounding the intermediate result
// x*y.
//
// Therefore we need to add explicit type conversions such as "float64(x)" to
// prevent optimizations that break correctness. Rather than adding them on a
// case-by-case basis as real correctness issues are discovered, we instead
// preemptively force them to be added everywhere by using this wrapper type
// for all floating-point math.
type F64 struct {
value float64
}
func NewF64(a float64) F64 {
return F64{value: float64(a)}
}
func (a F64) Value() float64 {
return a.value
}
func (a F64) IsNaN() bool {
return math.IsNaN(a.value)
}
func (a F64) Neg() F64 {
return NewF64(-a.value)
}
func (a F64) Abs() F64 {
return NewF64(math.Abs(a.value))
}
func (a F64) Sin() F64 {
return NewF64(math.Sin(a.value))
}
func (a F64) Cos() F64 {
return NewF64(math.Cos(a.value))
}
func (a F64) Log2() F64 {
return NewF64(math.Log2(a.value))
}
func (a F64) Round() F64 {
return NewF64(math.Round(a.value))
}
func (a F64) Floor() F64 {
return NewF64(math.Floor(a.value))
}
func (a F64) Ceil() F64 {
return NewF64(math.Ceil(a.value))
}
func (a F64) Squared() F64 {
return a.Mul(a)
}
func (a F64) Cubed() F64 {
return a.Mul(a).Mul(a)
}
func (a F64) Sqrt() F64 {
return NewF64(math.Sqrt(a.value))
}
func (a F64) Cbrt() F64 {
return NewF64(math.Cbrt(a.value))
}
func (a F64) Add(b F64) F64 {
return NewF64(a.value + b.value)
}
func (a F64) AddConst(b float64) F64 {
return NewF64(a.value + b)
}
func (a F64) Sub(b F64) F64 {
return NewF64(a.value - b.value)
}
func (a F64) SubConst(b float64) F64 {
return NewF64(a.value - b)
}
func (a F64) Mul(b F64) F64 {
return NewF64(a.value * b.value)
}
func (a F64) MulConst(b float64) F64 {
return NewF64(a.value * b)
}
func (a F64) Div(b F64) F64 {
return NewF64(a.value / b.value)
}
func (a F64) DivConst(b float64) F64 {
return NewF64(a.value / b)
}
func (a F64) Pow(b F64) F64 {
return NewF64(math.Pow(a.value, b.value))
}
func (a F64) PowConst(b float64) F64 {
return NewF64(math.Pow(a.value, b))
}
func (a F64) Atan2(b F64) F64 {
return NewF64(math.Atan2(a.value, b.value))
}
func (a F64) WithSignFrom(b F64) F64 {
return NewF64(math.Copysign(a.value, b.value))
}
func Min2(a F64, b F64) F64 {
return NewF64(math.Min(a.value, b.value))
}
func Max2(a F64, b F64) F64 {
return NewF64(math.Max(a.value, b.value))
}
func Min3(a F64, b F64, c F64) F64 {
return NewF64(math.Min(math.Min(a.value, b.value), c.value))
}
func Max3(a F64, b F64, c F64) F64 {
return NewF64(math.Max(math.Max(a.value, b.value), c.value))
}
func Lerp(a F64, b F64, t F64) F64 {
return b.Sub(a).Mul(t).Add(a)
}
|