github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/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 = 2.2250738585072014e-308 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 if x < 0 { 127 x = math.Ceil(intermed - 0.5) 128 } else { 129 x = math.Floor(intermed + 0.5) 130 } 131 132 if x == 0 { 133 return 0 134 } 135 136 return x / pow 137 } 138 139 // RoundEven returns the half even rounded value of x with prec precision. 140 // 141 // Special cases are: 142 // RoundEven(±0) = +0 143 // RoundEven(±Inf) = ±Inf 144 // RoundEven(NaN) = NaN 145 func RoundEven(x float64, prec int) float64 { 146 if x == 0 { 147 // Make sure zero is returned 148 // without the negative bit set. 149 return 0 150 } 151 // Fast path for positive precision on integers. 152 if prec >= 0 && x == math.Trunc(x) { 153 return x 154 } 155 pow := math.Pow10(prec) 156 intermed := x * pow 157 if math.IsInf(intermed, 0) { 158 return x 159 } 160 if isHalfway(intermed) { 161 correction, _ := math.Modf(math.Mod(intermed, 2)) 162 intermed += correction 163 if intermed > 0 { 164 x = math.Floor(intermed) 165 } else { 166 x = math.Ceil(intermed) 167 } 168 } else { 169 if x < 0 { 170 x = math.Ceil(intermed - 0.5) 171 } else { 172 x = math.Floor(intermed + 0.5) 173 } 174 } 175 176 if x == 0 { 177 return 0 178 } 179 180 return x / pow 181 } 182 183 func isHalfway(x float64) bool { 184 _, frac := math.Modf(x) 185 frac = math.Abs(frac) 186 return frac == 0.5 || (math.Nextafter(frac, math.Inf(-1)) < 0.5 && math.Nextafter(frac, math.Inf(1)) > 0.5) 187 } 188 189 // Same returns true when the inputs have the same value, allowing NaN equality. 190 func Same(a, b float64) bool { 191 return a == b || (math.IsNaN(a) && math.IsNaN(b)) 192 }