-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
logictest: compare floating point values approximately on s390x
On s390x in the std math package and some c-deps, floating point calculations can produce results that differ from the values calculated on amd64. This patch adds a function to compare logictest floating point and decimal values within a small relative margin on s390x. Release note: None
- Loading branch information
1 parent
aa4d1f0
commit adc2efc
Showing
7 changed files
with
260 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") | ||
|
||
go_library( | ||
name = "floatcmp", | ||
srcs = ["floatcmp.go"], | ||
importpath = "github.com/cockroachdb/cockroach/pkg/testutils/floatcmp", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"@com_github_google_go_cmp//cmp", | ||
"@com_github_google_go_cmp//cmp/cmpopts", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "floatcmp_test", | ||
size = "small", | ||
srcs = ["floatcmp_test.go"], | ||
embed = [":floatcmp"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
// Package floatcmp provides functions for determining float values to be equal | ||
// if they are within a tolerance. It is designed to be used in tests. | ||
package floatcmp | ||
|
||
import ( | ||
"github.com/google/go-cmp/cmp" | ||
"github.com/google/go-cmp/cmp/cmpopts" | ||
) | ||
|
||
const ( | ||
// CloseFraction can be used to set a "close" tolerance for the fraction | ||
// argument of functions in this package. It should typically be used with | ||
// the CloseMargin constant for the margin argument. Its value is taken from | ||
// the close tolerances in go's math package. | ||
CloseFraction float64 = 1e-14 | ||
// CloseMargin can be used to set a "close" tolerance for the margin | ||
// argument of functions in this package. It should typically be used with | ||
// the CloseFraction constant for the fraction argument. | ||
// | ||
// It is set to the square of CloseFraction so it only used when the smaller | ||
// of the absolute expected and actual values is in the range: | ||
// | ||
// -CloseFraction <= 0 <= CloseFraction | ||
// | ||
// CloseMargin is greater than 0 otherwise if either expected or actual were | ||
// 0 the calculated tolerance from the fraction would be 0. | ||
CloseMargin float64 = CloseFraction * CloseFraction | ||
) | ||
|
||
// EqualApprox reports whether expected and actual are deeply equal with the | ||
// following modifications for float64 and float32 types: | ||
// | ||
// • If both expected and actual are not NaN or infinate, they are equal within | ||
// the larger of the relative fraction or absolute margin calculated from the | ||
// fraction and margin arguments. | ||
// | ||
// • If both expected and actual are NaN, they are equal. | ||
// | ||
// Both fraction and margin must be non-negative. | ||
func EqualApprox(expected interface{}, actual interface{}, fraction float64, margin float64) bool { | ||
return cmp.Equal(expected, actual, cmpopts.EquateApprox(fraction, margin), cmpopts.EquateNaNs()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
package floatcmp | ||
|
||
import ( | ||
"math" | ||
"testing" | ||
) | ||
|
||
type ( | ||
floatArgs struct { | ||
expected float64 | ||
actual float64 | ||
} | ||
testStruct struct { | ||
X, Y float64 | ||
I int | ||
} | ||
structArgs struct { | ||
expected testStruct | ||
actual testStruct | ||
} | ||
floatTestCase struct { | ||
name string | ||
args floatArgs | ||
want bool | ||
} | ||
structTestCase struct { | ||
name string | ||
args structArgs | ||
want bool | ||
} | ||
) | ||
|
||
var floatTests = []floatTestCase{ | ||
{ | ||
name: "zeros", | ||
args: floatArgs{expected: 0, actual: 0}, | ||
want: true, | ||
}, | ||
{ | ||
name: "NaNs", | ||
args: floatArgs{expected: math.NaN(), actual: math.NaN()}, | ||
want: true, | ||
}, | ||
{ | ||
name: "zero not close to NaN", | ||
args: floatArgs{expected: 0, actual: math.NaN()}, | ||
want: false, | ||
}, | ||
{ | ||
name: "positive infinities", | ||
args: floatArgs{expected: math.Inf(+1), actual: math.Inf(+1)}, | ||
want: true, | ||
}, | ||
{ | ||
name: "negative infinities", | ||
args: floatArgs{expected: math.Inf(-1), actual: math.Inf(-1)}, | ||
want: true, | ||
}, | ||
{ | ||
name: "ones", | ||
args: floatArgs{expected: 1, actual: 1}, | ||
want: true, | ||
}, | ||
{ | ||
name: "signs", | ||
args: floatArgs{expected: 1, actual: -1}, | ||
want: false, | ||
}, | ||
{ | ||
name: "different", | ||
args: floatArgs{expected: 1, actual: 2}, | ||
want: false, | ||
}, | ||
{ | ||
name: "close to zero", | ||
args: floatArgs{expected: 0, actual: math.Nextafter(0+CloseMargin, math.Inf(-1))}, | ||
want: true, | ||
}, | ||
{ | ||
name: "not close to zero", | ||
args: floatArgs{expected: 0, actual: math.Nextafter(0+CloseMargin, math.Inf(+1))}, | ||
want: false, | ||
}, | ||
{ | ||
name: "close to CloseFraction", | ||
args: floatArgs{expected: CloseFraction, actual: math.Nextafter(CloseFraction+CloseMargin, math.Inf(-1))}, | ||
want: true, | ||
}, | ||
{ | ||
name: "not close to CloseFraction", | ||
args: floatArgs{expected: CloseFraction, actual: math.Nextafter(CloseFraction+CloseMargin, math.Inf(+1))}, | ||
want: false, | ||
}, | ||
{ | ||
name: "close to one", | ||
args: floatArgs{expected: 1, actual: math.Nextafter(1+1*CloseFraction, math.Inf(-1))}, | ||
want: true, | ||
}, | ||
{ | ||
name: "not close to one", | ||
args: floatArgs{expected: 1, actual: math.Nextafter(1+1*CloseFraction, math.Inf(+1))}, | ||
want: false, | ||
}, | ||
} | ||
|
||
func toStructTests(floatTestCases []floatTestCase) []structTestCase { | ||
structTestCases := make([]structTestCase, 0, len(floatTestCases)) | ||
for _, ft := range floatTestCases { | ||
structTestCases = append(structTestCases, | ||
structTestCase{ | ||
name: ft.name + " struct", | ||
args: structArgs{expected: testStruct{X: ft.args.expected, Y: ft.args.expected, I: 0}, actual: testStruct{X: ft.args.actual, Y: ft.args.actual, I: 0}}, | ||
want: ft.want, | ||
}) | ||
} | ||
return structTestCases | ||
} | ||
|
||
func TestEqualClose(t *testing.T) { | ||
for _, tt := range floatTests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := EqualApprox(tt.args.expected, tt.args.actual, CloseFraction, CloseMargin); got != tt.want { | ||
t.Errorf("Close(%.16e, %.16e) = %v, want %v", tt.args.expected, tt.args.actual, got, tt.want) | ||
} | ||
}) | ||
} | ||
for _, tt := range toStructTests(floatTests) { | ||
t.Run(tt.name, func(t *testing.T) { | ||
if got := EqualApprox(tt.args.expected, tt.args.actual, CloseFraction, CloseMargin); got != tt.want { | ||
t.Errorf("Close(%.v, %.v) = %v, want %v", tt.args.expected, tt.args.actual, got, tt.want) | ||
} | ||
}) | ||
} | ||
} |