Skip to content

Commit

Permalink
starlarktest/assert.star: allow +/-1 ULP in float eq comparisons (#490)
Browse files Browse the repository at this point in the history
Fixes #488
  • Loading branch information
adonovan committed Jul 25, 2023
1 parent 08598ae commit 0d72639
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 6 deletions.
10 changes: 9 additions & 1 deletion starlarktest/assert.star
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
# matches(str, pattern): report whether str matches regular expression pattern.
# module(**kwargs): a constructor for a module.
# _freeze(x): freeze the value x and everything reachable from it.
# _floateq(x, y): reports floating point equality (within 1 ULP).
#
# Clients may use these functions to define their own testing abstractions.

_num = ("float", "int")

def _eq(x, y):
if x != y:
error("%r != %r" % (x, y))
if (type(x) == "float" and type(y) in _num or
type(y) == "float" and type(x) in _num):
if not _floateq(float(x), float(y)):
error("floats: %r != %r (delta > 1 ulp)" % (x, y))
else:
error("%r != %r" % (x, y))

def _ne(x, y):
if x == y:
Expand Down
35 changes: 30 additions & 5 deletions starlarktest/starlarktest.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package starlarktest // import "go.starlark.net/starlarktest"
import (
_ "embed"
"fmt"
"math"
"os"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -63,11 +64,12 @@ var (
func LoadAssertModule() (starlark.StringDict, error) {
once.Do(func() {
predeclared := starlark.StringDict{
"error": starlark.NewBuiltin("error", error_),
"catch": starlark.NewBuiltin("catch", catch),
"matches": starlark.NewBuiltin("matches", matches),
"module": starlark.NewBuiltin("module", starlarkstruct.MakeModule),
"_freeze": starlark.NewBuiltin("freeze", freeze),
"error": starlark.NewBuiltin("error", error_),
"catch": starlark.NewBuiltin("catch", catch),
"matches": starlark.NewBuiltin("matches", matches),
"module": starlark.NewBuiltin("module", starlarkstruct.MakeModule),
"_freeze": starlark.NewBuiltin("freeze", freeze),
"_floateq": starlark.NewBuiltin("floateq", floateq),
}
thread := new(starlark.Thread)
assert, assertErr = starlark.ExecFile(thread, "assert.star", assertFileSrc, predeclared)
Expand Down Expand Up @@ -131,6 +133,29 @@ func freeze(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, k
return args[0], nil
}

// floateq(x, y) reports whether two floats are within 1 ULP of each other.
func floateq(thread *starlark.Thread, b *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
var xf, yf starlark.Float
if err := starlark.UnpackPositionalArgs(b.Name(), args, kwargs, 2, &xf, &yf); err != nil {
return nil, err
}

res := false
switch {
case xf == yf:
res = true
case math.IsNaN(float64(xf)):
res = math.IsNaN(float64(yf))
case math.IsNaN(float64(yf)):
// false (non-NaN = Nan)
default:
x := math.Float64bits(float64(xf))
y := math.Float64bits(float64(yf))
res = x == y+1 || y == x+1
}
return starlark.Bool(res), nil
}

// DataFile returns the effective filename of the specified
// test data resource. The function abstracts differences between
// 'go build', under which a test runs in its package directory,
Expand Down

0 comments on commit 0d72639

Please sign in to comment.