2025-03-06 13:01:21 -05:00

53 lines
1.4 KiB
Go

package helper
import (
"math"
)
// EqFloat64Fuzzy returns true if two floats aren't meaningfully distinct from each other.
//
// NaNs aren't considered distinct (meaning this function will return true if both inputs are NaN).
func EqFloat64Fuzzy(a, b float64) bool {
// if either input is NaN...
if math.IsNaN(a) || math.IsNaN(b) {
// return true if they'be both NaN (think SQL's "IS NOT DISTINCT FROM")
// otherwise was was NaN and the other was not, so return false.
return math.IsNaN(a) == math.IsNaN(b)
}
// if either input is infinity...
if math.IsInf(a, 0) || math.IsInf(b, 0) {
// return true if they're the same value (both +infinity or both -infinity)
// false otherwise (infinity vs a finite number, or an infinity with the opposite sign)
return a == b
}
const epsilon = 1e-9
absA, absB, absDiff := math.Abs(a), math.Abs(b), math.Abs(a-b)
// For numbers close to zero, use absolute epsilon
if min(absA, absB, absDiff) < math.SmallestNonzeroFloat64 {
return absDiff < epsilon
}
return absDiff < epsilon*max(absA, absB)
}
// EqFloat64SliceFuzzy returns true if two lists of floats aren't meaningfully distinct from each other.
//
// Returns false if the lists are of different lengths, [EqFloat64Fuzzy] returns false for any pair of floats.
func EqFloat64SliceFuzzy(a, b []float64) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !EqFloat64Fuzzy(a[i], b[i]) {
return false
}
}
return true
}