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  }