Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix float64 comparison test failure on archs using FMA #1133

Merged
merged 4 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 7 additions & 6 deletions prometheus/histogram_test.go
Expand Up @@ -28,6 +28,8 @@ import (
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"

"github.com/prometheus/client_golang/prometheus/internal"

dto "github.com/prometheus/client_model/go"
)

Expand Down Expand Up @@ -355,13 +357,12 @@ func TestBuckets(t *testing.T) {

got = ExponentialBucketsRange(1, 100, 10)
want = []float64{
1.0, 1.6681005372000588, 2.782559402207125,
4.641588833612779, 7.742636826811273, 12.915496650148842,
21.544346900318846, 35.93813663804629, 59.94842503189414,
100.00000000000007,
1.0, 1.6681, 2.7825, 4.6415, 7.7426, 12.9154, 21.5443,
35.9381, 59.9484, 100.0000,
}
if !reflect.DeepEqual(got, want) {
t.Errorf("exponential buckets range: got %v, want %v", got, want)
const epsilon = 0.0001
if !internal.AlmostEqualFloat64s(got, want, epsilon) {
t.Errorf("exponential buckets range: got %v, want %v (epsilon %f)", got, want, epsilon)
}
}

Expand Down
92 changes: 92 additions & 0 deletions prometheus/internal/difflib.go
Expand Up @@ -22,6 +22,7 @@ import (
"bytes"
"fmt"
"io"
"math"
"strings"
)

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

var (
kakkoyun marked this conversation as resolved.
Show resolved Hide resolved
// minNormalFloat64 is the smallest positive normal value of type float64.
minNormalFloat64 = math.Float64frombits(0x0010000000000000)

// minNormalFloat32 is the smallest positive normal value of type float32.
minNormalFloat32 = math.Float32frombits(0x00800000)
)

// AlmostEqualFloat64 returns true if a and b are equal within a relative error
// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the
// details of the applied method.
//
// This function is copy/paste to avoid a dependency.
// https://github.com/beorn7/floats
func AlmostEqualFloat64(a, b, epsilon float64) bool {
if a == b {
return true
}
absA := math.Abs(a)
absB := math.Abs(b)
diff := math.Abs(a - b)
if a == 0 || b == 0 || absA+absB < minNormalFloat64 {
return diff < epsilon*minNormalFloat64
}
return diff/math.Min(absA+absB, math.MaxFloat64) < epsilon
}

// AlmostEqualFloat64s is the slice form of AlmostEqualFloat64.
func AlmostEqualFloat64s(a, b []float64, epsilon float64) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !AlmostEqualFloat64(a[i], b[i], epsilon) {
return false
}
}
return true
}

// AlmostEqualFloat32 returns true if a and b are equal within a relative error
// of epsilon. See http://floating-point-gui.de/errors/comparison/ for the
// details of the applied method.
//
// This function is copy/paste to avoid a dependency.
// https://github.com/beorn7/floats
func AlmostEqualFloat32(a, b, epsilon float32) bool {
if a == b {
return true
}
absA := AbsFloat32(a)
absB := AbsFloat32(b)
diff := AbsFloat32(a - b)
if a == 0 || b == 0 || absA+absB < minNormalFloat32 {
return diff < epsilon*minNormalFloat32
}
return diff/MinFloat32(absA+absB, math.MaxFloat32) < epsilon
}

// AlmostEqualFloat32s is the slice form of AlmostEqualFloat32.
func AlmostEqualFloat32s(a, b []float32, epsilon float32) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if !AlmostEqualFloat32(a[i], b[i], epsilon) {
return false
}
}
return true
}

// AbsFloat32 works like math.Abs, but for float32.
func AbsFloat32(x float32) float32 {
switch {
case x < 0:
return -x
case x == 0:
return 0 // return correctly abs(-0)
}
return x
}

// MinFloat32 works like math.Min, but for float32.
func MinFloat32(x, y float32) float32 {
if x < y {
return x
}
return y
}

type Match struct {
A int
B int
Expand Down