github.com/gopherd/gonum@v0.0.4/floats/scalar/scalar.go (about) 1 // Copyright ©2013 The Gonum Authors. All rights reserved. 2 // Use of this code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package scalar 6 7 import ( 8 "math" 9 "strconv" 10 ) 11 12 // EqualWithinAbs returns true when a and b have an absolute difference 13 // not greater than tol. 14 func EqualWithinAbs(a, b, tol float64) bool { 15 return a == b || math.Abs(a-b) <= tol 16 } 17 18 // minNormalFloat64 is the smallest normal number. For 64 bit IEEE-754 19 // floats this is 2^{-1022}. 20 const minNormalFloat64 = 0x1p-1022 21 22 // EqualWithinRel returns true when the difference between a and b 23 // is not greater than tol times the greater absolute value of a and b, 24 // abs(a-b) <= tol * max(abs(a), abs(b)). 25 func EqualWithinRel(a, b, tol float64) bool { 26 if a == b { 27 return true 28 } 29 delta := math.Abs(a - b) 30 if delta <= minNormalFloat64 { 31 return delta <= tol*minNormalFloat64 32 } 33 // We depend on the division in this relationship to identify 34 // infinities (we rely on the NaN to fail the test) otherwise 35 // we compare Infs of the same sign and evaluate Infs as equal 36 // independent of sign. 37 return delta/math.Max(math.Abs(a), math.Abs(b)) <= tol 38 } 39 40 // EqualWithinAbsOrRel returns true when a and b are equal to within 41 // the absolute or relative tolerances. See EqualWithinAbs and 42 // EqualWithinRel for details. 43 func EqualWithinAbsOrRel(a, b, absTol, relTol float64) bool { 44 return EqualWithinAbs(a, b, absTol) || EqualWithinRel(a, b, relTol) 45 } 46 47 // EqualWithinULP returns true when a and b are equal to within 48 // the specified number of floating point units in the last place. 49 func EqualWithinULP(a, b float64, ulp uint) bool { 50 if a == b { 51 return true 52 } 53 if math.IsNaN(a) || math.IsNaN(b) { 54 return false 55 } 56 if math.Signbit(a) != math.Signbit(b) { 57 return math.Float64bits(math.Abs(a))+math.Float64bits(math.Abs(b)) <= uint64(ulp) 58 } 59 return ulpDiff(math.Float64bits(a), math.Float64bits(b)) <= uint64(ulp) 60 } 61 62 func ulpDiff(a, b uint64) uint64 { 63 if a > b { 64 return a - b 65 } 66 return b - a 67 } 68 69 const ( 70 nanBits = 0x7ff8000000000000 71 nanMask = 0xfff8000000000000 72 ) 73 74 // NaNWith returns an IEEE 754 "quiet not-a-number" value with the 75 // payload specified in the low 51 bits of payload. 76 // The NaN returned by math.NaN has a bit pattern equal to NaNWith(1). 77 func NaNWith(payload uint64) float64 { 78 return math.Float64frombits(nanBits | (payload &^ nanMask)) 79 } 80 81 // NaNPayload returns the lowest 51 bits payload of an IEEE 754 "quiet 82 // not-a-number". For values of f other than quiet-NaN, NaNPayload 83 // returns zero and false. 84 func NaNPayload(f float64) (payload uint64, ok bool) { 85 b := math.Float64bits(f) 86 if b&nanBits != nanBits { 87 return 0, false 88 } 89 return b &^ nanMask, true 90 } 91 92 // ParseWithNA converts the string s to a float64 in value. 93 // If s equals missing, weight is returned as 0, otherwise 1. 94 func ParseWithNA(s, missing string) (value, weight float64, err error) { 95 if s == missing { 96 return 0, 0, nil 97 } 98 value, err = strconv.ParseFloat(s, 64) 99 if err == nil { 100 weight = 1 101 } 102 return value, weight, err 103 } 104 105 // Round returns the half away from zero rounded value of x with prec precision. 106 // 107 // Special cases are: 108 // Round(±0) = +0 109 // Round(±Inf) = ±Inf 110 // Round(NaN) = NaN 111 func Round(x float64, prec int) float64 { 112 if x == 0 { 113 // Make sure zero is returned 114 // without the negative bit set. 115 return 0 116 } 117 // Fast path for positive precision on integers. 118 if prec >= 0 && x == math.Trunc(x) { 119 return x 120 } 121 pow := math.Pow10(prec) 122 intermed := x * pow 123 if math.IsInf(intermed, 0) { 124 return x 125 } 126 x = math.Round(intermed) 127 128 if x == 0 { 129 return 0 130 } 131 132 return x / pow 133 } 134 135 // RoundEven returns the half even rounded value of x with prec precision. 136 // 137 // Special cases are: 138 // RoundEven(±0) = +0 139 // RoundEven(±Inf) = ±Inf 140 // RoundEven(NaN) = NaN 141 func RoundEven(x float64, prec int) float64 { 142 if x == 0 { 143 // Make sure zero is returned 144 // without the negative bit set. 145 return 0 146 } 147 // Fast path for positive precision on integers. 148 if prec >= 0 && x == math.Trunc(x) { 149 return x 150 } 151 pow := math.Pow10(prec) 152 intermed := x * pow 153 if math.IsInf(intermed, 0) { 154 return x 155 } 156 x = math.RoundToEven(intermed) 157 158 if x == 0 { 159 return 0 160 } 161 162 return x / pow 163 } 164 165 // Same returns true when the inputs have the same value, allowing NaN equality. 166 func Same(a, b float64) bool { 167 return a == b || (math.IsNaN(a) && math.IsNaN(b)) 168 }