github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/utils/unittest/math.go (about)

     1  package unittest
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  )
     9  
    10  // RequireNumericallyClose is a wrapper around require.Equal that allows for a small epsilon difference between
    11  // two floats. This is useful when comparing floats that are the result of a computation. For example, when comparing
    12  // the result of a computation with a constant.
    13  // The epsilon is calculated as:
    14  //
    15  //	epsilon = max(|a|, |b|) * epsilon
    16  //
    17  // Example:
    18  //
    19  //	RequireNumericallyClose(t, 1.0, 1.1, 0.1) // passes since 1.0 * 0.1 = 0.1 < 0.1
    20  //	RequireNumericallyClose(t, 1.0, 1.1, 0.01) // fails since 1.0 * 0.01 = 0.01 < 0.1
    21  //	RequireNumericallyClose(t, 1.0, 1.1, 0.11) // fails since 1.1 * 0.11 = 0.121 > 0.1
    22  //
    23  // Args:
    24  //
    25  //		t: the testing.TB instance
    26  //	 a: the first float
    27  //	 b: the second float
    28  func RequireNumericallyClose(t testing.TB, a, b float64, epsilon float64, msgAndArgs ...interface{}) {
    29  	require.True(t, AreNumericallyClose(a, b, epsilon), msgAndArgs...)
    30  }
    31  
    32  // AreNumericallyClose returns true if the two floats are within epsilon of each other.
    33  // The epsilon is calculated as:
    34  //
    35  //	epsilon = max(|a|, |b|) * epsilon
    36  //
    37  // Example:
    38  //
    39  //	AreNumericallyClose(1.0, 1.1, 0.1) // true since 1.0 * 0.1 = 0.1 < 0.1
    40  //	AreNumericallyClose(1.0, 1.1, 0.01) // false since 1.0 * 0.01 = 0.01 < 0.1
    41  //	AreNumericallyClose(1.0, 1.1, 0.11) // false since 1.1 * 0.11 = 0.121 > 0.1
    42  //
    43  // Args:
    44  // a: the first float
    45  // b: the second float
    46  // epsilon: the epsilon value
    47  // Returns:
    48  // true if the two floats are within epsilon of each other
    49  // false otherwise
    50  func AreNumericallyClose(a, b float64, epsilon float64) bool {
    51  	if a == float64(0) {
    52  		return math.Abs(b) <= epsilon
    53  	}
    54  	if b == float64(0) {
    55  		return math.Abs(a) <= epsilon
    56  	}
    57  
    58  	nominator := math.Abs(a - b)
    59  	denominator := math.Max(math.Abs(a), math.Abs(b))
    60  	return nominator/denominator <= epsilon
    61  }