// 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 scalar import ( "math" "strconv" ) // EqualWithinAbs returns true when a and b have an absolute difference // not greater than tol. func EqualWithinAbs(a, b, tol float64) bool { return a == b || math.Abs(a-b) <= tol } // minNormalFloat64 is the smallest normal number. For 64 bit IEEE-754 // floats this is 2^{-1022}. const minNormalFloat64 = 0x1p-1022 // EqualWithinRel returns true when 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, tol float64) bool { if a == b { return true } delta := math.Abs(a - b) if delta <= minNormalFloat64 { return delta <= tol*minNormalFloat64 } // We depend on the division in this relationship to identify // infinities (we rely on the NaN to fail the test) otherwise // we compare Infs of the same sign and evaluate Infs as equal // independent of sign. return delta/math.Max(math.Abs(a), math.Abs(b)) <= tol } // EqualWithinAbsOrRel returns true when a and b are equal to within // the absolute or relative tolerances. See EqualWithinAbs and // EqualWithinRel for details. func EqualWithinAbsOrRel(a, b, absTol, relTol float64) bool { return EqualWithinAbs(a, b, absTol) || EqualWithinRel(a, b, relTol) } // EqualWithinULP returns true when a and b are equal to within // the specified number of floating point units in the last place. func EqualWithinULP(a, b float64, ulp uint) bool { if a == b { return true } if math.IsNaN(a) || math.IsNaN(b) { return false } if math.Signbit(a) != math.Signbit(b) { return math.Float64bits(math.Abs(a))+math.Float64bits(math.Abs(b)) <= uint64(ulp) } return ulpDiff(math.Float64bits(a), math.Float64bits(b)) <= uint64(ulp) } func ulpDiff(a, b uint64) uint64 { if a > b { return a - b } return b - a } const ( nanBits = 0x7ff8000000000000 nanMask = 0xfff8000000000000 ) // NaNWith returns an IEEE 754 "quiet not-a-number" value with the // payload specified in the low 51 bits of payload. // The NaN returned by math.NaN has a bit pattern equal to NaNWith(1). func NaNWith(payload uint64) float64 { return math.Float64frombits(nanBits | (payload &^ nanMask)) } // NaNPayload returns the lowest 51 bits payload of an IEEE 754 "quiet // not-a-number". For values of f other than quiet-NaN, NaNPayload // returns zero and false. func NaNPayload(f float64) (payload uint64, ok bool) { b := math.Float64bits(f) if b&nanBits != nanBits { return 0, false } return b &^ nanMask, true } // ParseWithNA converts the string s to a float64 in value. // If s equals missing, weight is returned as 0, otherwise 1. func ParseWithNA(s, missing string) (value, weight float64, err error) { if s == missing { return 0, 0, nil } value, err = strconv.ParseFloat(s, 64) if err == nil { weight = 1 } return value, weight, 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 float64, prec int) float64 { if x == 0 { // Make sure zero is returned // without the negative bit set. return 0 } // Fast path for positive precision on integers. if prec >= 0 && x == math.Trunc(x) { return x } pow := math.Pow10(prec) intermed := x * pow if math.IsInf(intermed, 0) { return x } if x < 0 { x = math.Ceil(intermed - 0.5) } else { x = math.Floor(intermed + 0.5) } if x == 0 { return 0 } return x / pow } // 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 float64, prec int) float64 { if x == 0 { // Make sure zero is returned // without the negative bit set. return 0 } // Fast path for positive precision on integers. if prec >= 0 && x == math.Trunc(x) { return x } pow := math.Pow10(prec) intermed := x * pow if math.IsInf(intermed, 0) { return x } if isHalfway(intermed) { correction, _ := math.Modf(math.Mod(intermed, 2)) intermed += correction if intermed > 0 { x = math.Floor(intermed) } else { x = math.Ceil(intermed) } } else { if x < 0 { x = math.Ceil(intermed - 0.5) } else { x = math.Floor(intermed + 0.5) } } if x == 0 { return 0 } return x / pow } func isHalfway(x float64) bool { _, frac := math.Modf(x) frac = math.Abs(frac) return frac == 0.5 || (math.Nextafter(frac, math.Inf(-1)) < 0.5 && math.Nextafter(frac, math.Inf(1)) > 0.5) } // Same returns true when the inputs have the same value, allowing NaN equality. func Same(a, b float64) bool { return a == b || (math.IsNaN(a) && math.IsNaN(b)) }