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