Skip to content

Commit e672c58

Browse files
committed
copy float compare dependency
Per discussion in the pull request, we'd like to avoid having an extra dependency on a float comparison package. Instead, we copy the float compare functions from the float comparison package. The float comparison package we're choosing is this. The author of this package has commented in the pull request and it looks like we have consensus that this is the best option. github.com/beorn7/floats Signed-off-by: Seth Bunce <[email protected]>
1 parent 0d8d662 commit e672c58

File tree

2 files changed

+97
-23
lines changed

2 files changed

+97
-23
lines changed

prometheus/histogram_test.go

+5-23
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import (
2828
"github.com/golang/protobuf/proto"
2929
"google.golang.org/protobuf/types/known/timestamppb"
3030

31+
"github.com/prometheus/client_golang/prometheus/internal"
32+
3133
dto "github.com/prometheus/client_model/go"
3234
)
3335

@@ -358,9 +360,9 @@ func TestBuckets(t *testing.T) {
358360
1.0, 1.6681, 2.7825, 4.6415, 7.7426, 12.9154, 21.5443,
359361
35.9381, 59.9484, 100.0000,
360362
}
361-
const tolerance = 0.0001
362-
if !equalFloat64s(got, want, tolerance) {
363-
t.Errorf("exponential buckets range: got %v, want %v (tolerance %f)", got, want, tolerance)
363+
const epsilon = 0.0001
364+
if !internal.AlmostEqualFloat64s(got, want, epsilon) {
365+
t.Errorf("exponential buckets range: got %v, want %v (epsilon %f)", got, want, epsilon)
364366
}
365367
}
366368

@@ -466,23 +468,3 @@ func TestHistogramExemplar(t *testing.T) {
466468
}
467469
}
468470
}
469-
470-
// equalFloat64 returns true if a and b are within the tolerance. We have this
471-
// because float comparison varies on different architectures. For example,
472-
// architectures which do FMA yield slightly different results.
473-
// https://github.com/prometheus/client_golang/pull/899#issuecomment-1244506390
474-
func equalFloat64(a, b, tolerance float64) bool {
475-
return math.Abs(a-b) < tolerance
476-
}
477-
478-
func equalFloat64s(a, b []float64, tolerance float64) bool {
479-
if len(a) != len(b) {
480-
return false
481-
}
482-
for i := range a {
483-
if !equalFloat64(a[i], b[i], tolerance) {
484-
return false
485-
}
486-
}
487-
return true
488-
}

prometheus/internal/difflib.go

+92
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"bytes"
2323
"fmt"
2424
"io"
25+
"math"
2526
"strings"
2627
)
2728

@@ -46,6 +47,97 @@ func calculateRatio(matches, length int) float64 {
4647
return 1.0
4748
}
4849

50+
var (
51+
// minNormalFloat64 is the smallest positive normal value of type float64.
52+
minNormalFloat64 = math.Float64frombits(0x0010000000000000)
53+
54+
// minNormalFloat32 is the smallest positive normal value of type float32.
55+
minNormalFloat32 = math.Float32frombits(0x00800000)
56+
)
57+
58+
// AlmostEqualFloat64 returns true if a and b are equal within a relative error
59+
// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the
60+
// details of the applied method.
61+
//
62+
// This function is copy/paste to avoid a dependency.
63+
// https://github.com/beorn7/floats
64+
func AlmostEqualFloat64(a, b, epsilon float64) bool {
65+
if a == b {
66+
return true
67+
}
68+
absA := math.Abs(a)
69+
absB := math.Abs(b)
70+
diff := math.Abs(a - b)
71+
if a == 0 || b == 0 || absA+absB < minNormalFloat64 {
72+
return diff < epsilon*minNormalFloat64
73+
}
74+
return diff/math.Min(absA+absB, math.MaxFloat64) < epsilon
75+
}
76+
77+
// AlmostEqualFloat64s is the slice form of AlmostEqualFloat64.
78+
func AlmostEqualFloat64s(a, b []float64, epsilon float64) bool {
79+
if len(a) != len(b) {
80+
return false
81+
}
82+
for i := range a {
83+
if !AlmostEqualFloat64(a[i], b[i], epsilon) {
84+
return false
85+
}
86+
}
87+
return true
88+
}
89+
90+
// AlmostEqualFloat32 returns true if a and b are equal within a relative error
91+
// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the
92+
// details of the applied method.
93+
//
94+
// This function is copy/paste to avoid a dependency.
95+
// https://github.com/beorn7/floats
96+
func AlmostEqualFloat32(a, b, epsilon float32) bool {
97+
if a == b {
98+
return true
99+
}
100+
absA := AbsFloat32(a)
101+
absB := AbsFloat32(b)
102+
diff := AbsFloat32(a - b)
103+
if a == 0 || b == 0 || absA+absB < minNormalFloat32 {
104+
return diff < epsilon*minNormalFloat32
105+
}
106+
return diff/MinFloat32(absA+absB, math.MaxFloat32) < epsilon
107+
}
108+
109+
// AlmostEqualFloat32s is the slice form of AlmostEqualFloat32.
110+
func AlmostEqualFloat32s(a, b []float32, epsilon float32) bool {
111+
if len(a) != len(b) {
112+
return false
113+
}
114+
for i := range a {
115+
if !AlmostEqualFloat32(a[i], b[i], epsilon) {
116+
return false
117+
}
118+
}
119+
return true
120+
}
121+
122+
// AbsFloat32 works like math.Abs, but for float32.
123+
func AbsFloat32(x float32) float32 {
124+
switch {
125+
case x < 0:
126+
return -x
127+
case x == 0:
128+
return 0 // return correctly abs(-0)
129+
}
130+
return x
131+
}
132+
133+
// MinFloat32 works like math.Min, but for float32.
134+
func MinFloat32(x, y float32) float32 {
135+
if x < y {
136+
return x
137+
}
138+
return y
139+
}
140+
49141
type Match struct {
50142
A int
51143
B int

0 commit comments

Comments
 (0)