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  }