From e486b124e0e541bd8ce8ca1e8f53c27c18d9b277 Mon Sep 17 00:00:00 2001 From: Richard Dingwall Date: Sat, 23 May 2020 18:32:17 -0400 Subject: [PATCH] Allow 1 ULP of error in GDA tests In the GDA arithmetic specification, exp, ln, log10 and power operations may be up to 1 ulp (unit in last place) in error. --- dectest/test.go | 46 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/dectest/test.go b/dectest/test.go index 7658ac9..36a4387 100644 --- a/dectest/test.go +++ b/dectest/test.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "strconv" + "strings" "testing" "github.com/ericlagergren/decimal" @@ -277,15 +278,15 @@ got : %v func check(t *testing.T, z, r *decimal.Big, c *Case, flags decimal.Condition) { helper(t)() if !equal(z, r) { - str := fmt.Sprintf(`%s -wanted: %q (%s:%d) -got : %q (%s:%d) -`, + s := []string{ c.ShortString(10000), - r, flags, -r.Scale(), - z, z.Context.Conditions, -z.Scale(), - ) - t.Log(str) + fmt.Sprintf("wanted: %q (%s:%d)", r, flags, -r.Scale()), + fmt.Sprintf("got : %q (%s:%d)", z, z.Context.Conditions, -z.Scale()), + } + if allow1ULPError[c.Op] && equalWithin1ULP(z, r) { + s = append(s, "passed within 1 ULP") + } + t.Log(strings.Join(s, "\n"), "\n") } } @@ -321,6 +322,35 @@ func equal(x, y *decimal.Big) bool { return cmp && scl && prec } +func equalWithin1ULP(x, y *decimal.Big) bool { + if !x.IsFinite() || !y.IsFinite() { + return false + } + formX, negX, coeffX, expX := x.Decompose(make([]byte, 2<<3)) + formY, negY, coeffY, expY := y.Decompose(make([]byte, 2<<3)) + if formX != formY || negX != negY || expX != expY { + return false + } + l := len(coeffX) + if len(coeffY) != l { + return false + } + for i := 0; i < l-1; i++ { + if coeffX[i] != coeffY[i] { + return false + } + } + d := coeffX[l-1] - coeffY[l-1] + return d == 0x01 || d == 0xff +} + +var allow1ULPError = map[Op]bool{ + Exp: true, + Ln: true, + Log10: true, + Power: true, +} + // helper returns testing.T.Helper, if it exists. func helper(v interface{}) func() { if fn, ok := v.(interface {