...

Source file src/github.com/cockroachdb/apd/v3/gda_test.go

Documentation: github.com/cockroachdb/apd/v3

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied. See the License for the specific language governing
    13  // permissions and limitations under the License.
    14  
    15  package apd
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"flag"
    21  	"fmt"
    22  	"io"
    23  	"os"
    24  	"path/filepath"
    25  	"sort"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  )
    32  
    33  const testDir = "testdata"
    34  
    35  var (
    36  	flagFailFast   = flag.Bool("fast", false, "stop work after first error; disables parallel testing")
    37  	flagIgnore     = flag.Bool("ignore", false, "print ignore lines on errors")
    38  	flagNoParallel = flag.Bool("noparallel", false, "disables parallel testing")
    39  	flagTime       = flag.Duration("time", 0, "interval at which to print long-running functions; 0 disables")
    40  )
    41  
    42  type TestCase struct {
    43  	Precision                int
    44  	MaxExponent, MinExponent int
    45  	Rounding                 string
    46  	Extended, Clamp          bool
    47  
    48  	ID         string
    49  	Operation  string
    50  	Operands   []string
    51  	Result     string
    52  	Conditions []string
    53  }
    54  
    55  func (tc TestCase) HasNull() bool {
    56  	if tc.Result == "#" {
    57  		return true
    58  	}
    59  	for _, o := range tc.Operands {
    60  		if o == "#" {
    61  			return true
    62  		}
    63  	}
    64  	return false
    65  }
    66  
    67  func (tc TestCase) SkipPrecision() bool {
    68  	switch tc.Operation {
    69  	case "tosci", "toeng", "apply":
    70  		return false
    71  	default:
    72  		return true
    73  	}
    74  }
    75  
    76  func ParseDecTest(r io.Reader) ([]TestCase, error) {
    77  	scanner := bufio.NewScanner(r)
    78  	tc := TestCase{
    79  		Extended: true,
    80  	}
    81  	var err error
    82  	var res []TestCase
    83  
    84  	for scanner.Scan() {
    85  		text := scanner.Text()
    86  		// TODO(mjibson): support these test cases
    87  		if strings.Contains(text, "#") {
    88  			continue
    89  		}
    90  		line := strings.Fields(strings.ToLower(text))
    91  		for i, t := range line {
    92  			if strings.HasPrefix(t, "--") {
    93  				line = line[:i]
    94  				break
    95  			}
    96  		}
    97  		if len(line) == 0 {
    98  			continue
    99  		}
   100  		if strings.HasSuffix(line[0], ":") {
   101  			if len(line) != 2 {
   102  				return nil, fmt.Errorf("expected 2 tokens, got %q", text)
   103  			}
   104  			switch directive := line[0]; directive[:len(directive)-1] {
   105  			case "precision":
   106  				tc.Precision, err = strconv.Atoi(line[1])
   107  				if err != nil {
   108  					return nil, err
   109  				}
   110  			case "maxexponent":
   111  				tc.MaxExponent, err = strconv.Atoi(line[1])
   112  				if err != nil {
   113  					return nil, err
   114  				}
   115  			case "minexponent":
   116  				tc.MinExponent, err = strconv.Atoi(line[1])
   117  				if err != nil {
   118  					return nil, err
   119  				}
   120  			case "rounding":
   121  				tc.Rounding = line[1]
   122  			case "version":
   123  				// ignore
   124  			case "extended":
   125  				tc.Extended = line[1] == "1"
   126  			case "clamp":
   127  				tc.Clamp = line[1] == "1"
   128  			default:
   129  				return nil, fmt.Errorf("unsupported directive: %s", directive)
   130  			}
   131  		} else {
   132  			if len(line) < 5 {
   133  				return nil, fmt.Errorf("short test case line: %q", text)
   134  			}
   135  			tc.ID = line[0]
   136  			tc.Operation = line[1]
   137  			tc.Operands = nil
   138  			var ops []string
   139  			line = line[2:]
   140  			for i, o := range line {
   141  				if o == "->" {
   142  					tc.Operands = ops
   143  					line = line[i+1:]
   144  					break
   145  				}
   146  				o = cleanNumber(o)
   147  				ops = append(ops, o)
   148  			}
   149  			if tc.Operands == nil || len(line) < 1 {
   150  				return nil, fmt.Errorf("bad test case line: %q", text)
   151  			}
   152  			tc.Result = strings.ToUpper(cleanNumber(line[0]))
   153  			tc.Conditions = line[1:]
   154  			res = append(res, tc)
   155  		}
   156  	}
   157  	if err := scanner.Err(); err != nil {
   158  		return nil, err
   159  	}
   160  	return res, nil
   161  }
   162  
   163  func cleanNumber(s string) string {
   164  	if len(s) > 1 && s[0] == '\'' && s[len(s)-1] == '\'' {
   165  		s = s[1 : len(s)-1]
   166  		s = strings.Replace(s, `''`, `'`, -1)
   167  	} else if len(s) > 1 && s[0] == '"' && s[len(s)-1] == '"' {
   168  		s = s[1 : len(s)-1]
   169  		s = strings.Replace(s, `""`, `"`, -1)
   170  	}
   171  	return s
   172  }
   173  
   174  // Copy ioutil.ReadDir to avoid staticcheck warning on go1.19 and above. Replace
   175  // with a call to os.ReadDir when we remove support for go1.15 and below.
   176  func ReadDir(dirname string) ([]os.FileInfo, error) {
   177  	f, err := os.Open(dirname)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	list, err := f.Readdir(-1)
   182  	f.Close()
   183  	if err != nil {
   184  		return nil, err
   185  	}
   186  	sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() })
   187  	return list, nil
   188  }
   189  
   190  func TestParseDecTest(t *testing.T) {
   191  	files, err := ReadDir(testDir)
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	for _, fi := range files {
   196  		t.Run(fi.Name(), func(t *testing.T) {
   197  			f, err := os.Open(filepath.Join(testDir, fi.Name()))
   198  			if err != nil {
   199  				t.Fatal(err)
   200  			}
   201  			defer f.Close()
   202  			_, err = ParseDecTest(f)
   203  			if err != nil {
   204  				t.Fatal(err)
   205  			}
   206  		})
   207  	}
   208  }
   209  
   210  var GDAfiles = []string{
   211  	"abs",
   212  	"add",
   213  	"base",
   214  	"compare",
   215  	"comparetotal",
   216  	"divide",
   217  	"divideint",
   218  	"exp",
   219  	"ln",
   220  	"log10",
   221  	"minus",
   222  	"multiply",
   223  	"plus",
   224  	"power",
   225  	"powersqrt",
   226  	"quantize",
   227  	"randoms",
   228  	"reduce",
   229  	"remainder",
   230  	"rounding",
   231  	"squareroot",
   232  	"subtract",
   233  	"tointegral",
   234  	"tointegralx",
   235  
   236  	// non-GDA tests
   237  	"cuberoot-apd",
   238  }
   239  
   240  func TestGDA(t *testing.T) {
   241  	var buf bytes.Buffer
   242  	fmt.Fprintf(&buf, "%10s%8s%8s%8s%8s%8s%8s\n", "name", "total", "success", "fail", "ignore", "skip", "missing")
   243  	for _, fname := range GDAfiles {
   244  		succeed := t.Run(fname, func(t *testing.T) {
   245  			path, tcs := readGDA(t, fname)
   246  			gdaTest(t, path, tcs)
   247  		})
   248  		if !succeed && *flagFailFast {
   249  			break
   250  		}
   251  	}
   252  }
   253  
   254  func (tc TestCase) Run(c *Context, done chan error, d, x, y *Decimal) (res Condition, err error) {
   255  	switch tc.Operation {
   256  	case "abs":
   257  		res, err = c.Abs(d, x)
   258  	case "add":
   259  		res, err = c.Add(d, x, y)
   260  	case "compare":
   261  		res, err = c.Cmp(d, x, y)
   262  	case "cuberoot":
   263  		res, err = c.Cbrt(d, x)
   264  	case "divide":
   265  		res, err = c.Quo(d, x, y)
   266  	case "divideint":
   267  		res, err = c.QuoInteger(d, x, y)
   268  	case "exp":
   269  		res, err = c.Exp(d, x)
   270  	case "ln":
   271  		res, err = c.Ln(d, x)
   272  	case "log10":
   273  		res, err = c.Log10(d, x)
   274  	case "minus":
   275  		res, err = c.Neg(d, x)
   276  	case "multiply":
   277  		res, err = c.Mul(d, x, y)
   278  	case "plus":
   279  		res, err = c.Add(d, x, decimalZero)
   280  	case "power":
   281  		res, err = c.Pow(d, x, y)
   282  	case "quantize":
   283  		res, err = c.Quantize(d, x, y.Exponent)
   284  	case "reduce":
   285  		_, res, err = c.Reduce(d, x)
   286  	case "remainder":
   287  		res, err = c.Rem(d, x, y)
   288  	case "squareroot":
   289  		res, err = c.Sqrt(d, x)
   290  	case "subtract":
   291  		res, err = c.Sub(d, x, y)
   292  	case "tointegral":
   293  		res, err = c.RoundToIntegralValue(d, x)
   294  	case "tointegralx":
   295  		res, err = c.RoundToIntegralExact(d, x)
   296  
   297  	// Below used only in benchmarks. Tests call it themselves.
   298  	case "comparetotal":
   299  		x.CmpTotal(y)
   300  	case "tosci":
   301  		_ = x.String()
   302  
   303  	default:
   304  		done <- fmt.Errorf("unknown operation: %s", tc.Operation)
   305  	}
   306  	return
   307  }
   308  
   309  // BenchmarkGDA benchmarks a GDA test. It should not be used without specifying
   310  // a sub-benchmark to run. For example:
   311  // go test -run XX -bench GDA/squareroot
   312  func BenchmarkGDA(b *testing.B) {
   313  	for _, fname := range GDAfiles {
   314  		b.Run(fname, func(b *testing.B) {
   315  			type benchCase struct {
   316  				tc  TestCase
   317  				ctx *Context
   318  				ops [2]*Decimal
   319  			}
   320  			_, tcs := readGDA(b, fname)
   321  			bcs := make([]benchCase, 0, len(tcs))
   322  		Loop:
   323  			for _, tc := range tcs {
   324  				if GDAignore[tc.ID] || tc.Result == "?" || tc.HasNull() {
   325  					continue
   326  				}
   327  				switch tc.Operation {
   328  				case "apply", "toeng":
   329  					continue
   330  				}
   331  				bc := benchCase{
   332  					tc:  tc,
   333  					ctx: tc.Context(b),
   334  				}
   335  				for i, o := range tc.Operands {
   336  					d, _, err := NewFromString(o)
   337  					if err != nil {
   338  						continue Loop
   339  					}
   340  					bc.ops[i] = d
   341  				}
   342  				bcs = append(bcs, bc)
   343  			}
   344  
   345  			// Translate inputs and outputs to Decimal vectors.
   346  			op1s := make([]Decimal, len(bcs))
   347  			op2s := make([]Decimal, len(bcs))
   348  			res := make([]Decimal, b.N*len(bcs))
   349  			for i, bc := range bcs {
   350  				op1s[i].Set(bc.ops[0])
   351  				if bc.ops[1] != nil {
   352  					op2s[i].Set(bc.ops[1])
   353  				}
   354  			}
   355  
   356  			b.ResetTimer()
   357  			for i := 0; i < b.N; i++ {
   358  				for j, bc := range bcs {
   359  					// Ignore errors here because the full tests catch them.
   360  					_, _ = bc.tc.Run(bc.ctx, nil, &res[i*len(bcs)+j], &op1s[j], &op2s[j])
   361  				}
   362  			}
   363  		})
   364  	}
   365  }
   366  
   367  func readGDA(t testing.TB, name string) (string, []TestCase) {
   368  	path := filepath.Join(testDir, name+".decTest")
   369  	f, err := os.Open(path)
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  	defer f.Close()
   374  	tcs, err := ParseDecTest(f)
   375  	if err != nil {
   376  		t.Fatal(err)
   377  	}
   378  	return path, tcs
   379  }
   380  
   381  func (tc TestCase) Context(t testing.TB) *Context {
   382  	rounding := Rounder(tc.Rounding)
   383  	if _, ok := roundings[rounding]; !ok {
   384  		t.Fatalf("unsupported rounding mode %s", tc.Rounding)
   385  	}
   386  	c := &Context{
   387  		Precision:   uint32(tc.Precision),
   388  		MaxExponent: int32(tc.MaxExponent),
   389  		MinExponent: int32(tc.MinExponent),
   390  		Rounding:    rounding,
   391  		Traps:       0,
   392  	}
   393  	return c
   394  }
   395  
   396  func gdaTest(t *testing.T, path string, tcs []TestCase) {
   397  	for _, tc := range tcs {
   398  		tc := tc
   399  		succeed := t.Run(tc.ID, func(t *testing.T) {
   400  			if *flagTime > 0 {
   401  				timeDone := make(chan struct{}, 1)
   402  				go func() {
   403  					start := time.Now()
   404  					for {
   405  						select {
   406  						case <-timeDone:
   407  							return
   408  						case <-time.After(*flagTime):
   409  							fmt.Println(tc.ID, "running for", time.Since(start))
   410  						}
   411  					}
   412  				}()
   413  				defer func() { timeDone <- struct{}{} }()
   414  			}
   415  			defer func() {
   416  				if t.Failed() {
   417  					if *flagIgnore {
   418  						tc.PrintIgnore()
   419  					}
   420  				}
   421  			}()
   422  			if GDAignore[tc.ID] {
   423  				t.Skip("ignored")
   424  			}
   425  			if tc.HasNull() {
   426  				t.Skip("has null")
   427  			}
   428  			switch tc.Operation {
   429  			case "toeng", "apply":
   430  				t.Skip("unsupported")
   431  			}
   432  			if !*flagNoParallel && !*flagFailFast {
   433  				t.Parallel()
   434  			}
   435  			// helpful acme address link
   436  			t.Logf("%s:/^%s ", path, tc.ID)
   437  			t.Logf("%s %s = %s (%s)", tc.Operation, strings.Join(tc.Operands, " "), tc.Result, strings.Join(tc.Conditions, " "))
   438  			t.Logf("prec: %d, round: %s, Emax: %d, Emin: %d", tc.Precision, tc.Rounding, tc.MaxExponent, tc.MinExponent)
   439  			operands := make([]*Decimal, 2)
   440  			c := tc.Context(t)
   441  			var res, opres Condition
   442  			opctx := c
   443  			if tc.SkipPrecision() {
   444  				opctx = opctx.WithPrecision(1000)
   445  				opctx.MaxExponent = MaxExponent
   446  				opctx.MinExponent = MinExponent
   447  			}
   448  			for i, o := range tc.Operands {
   449  				d, ores, err := opctx.NewFromString(o)
   450  				expectError := tc.Result == "NAN" && strings.Join(tc.Conditions, "") == "conversion_syntax"
   451  				if err != nil {
   452  					if expectError {
   453  						// Successfully detected bad syntax.
   454  						return
   455  					}
   456  					switch tc.Operation {
   457  					case "tosci":
   458  						// Skip cases with exponents larger than we will parse.
   459  						if strings.Contains(err.Error(), "value out of range") {
   460  							return
   461  						}
   462  					}
   463  					testExponentError(t, err)
   464  					if tc.Result == "?" {
   465  						return
   466  					}
   467  					t.Logf("%v, %v, %v", tc.Result, tc.Conditions, tc.Operation)
   468  					t.Fatalf("operand %d: %s: %+v", i, o, err)
   469  				} else if expectError {
   470  					t.Fatalf("expected error, got %s", d)
   471  				}
   472  				operands[i] = d
   473  				opres |= ores
   474  			}
   475  			switch tc.Operation {
   476  			case "power":
   477  				tmp := new(Decimal).Abs(operands[1])
   478  				// We don't handle power near the max exp limit.
   479  				if tmp.Cmp(New(MaxExponent, 0)) >= 0 {
   480  					t.Skip("x ** large y")
   481  				}
   482  				if tmp.Cmp(New(int64(c.MaxExponent), 0)) >= 0 {
   483  					t.Skip("x ** large y")
   484  				}
   485  			case "quantize":
   486  				if operands[1].Form != Finite {
   487  					t.Skip("quantize requires finite second operand")
   488  				}
   489  			}
   490  			var s string
   491  			// Fill d with bogus data to make sure all fields are correctly set.
   492  			d := &Decimal{
   493  				Form:     -2,
   494  				Negative: true,
   495  				Exponent: -6437897,
   496  			}
   497  			// Use d1 and d2 to verify that the result can be the same as the first and
   498  			// second operand.
   499  			var d1, d2 *Decimal
   500  			d.Coeff.SetInt64(9221)
   501  			start := time.Now()
   502  			defer func() {
   503  				t.Logf("duration: %s", time.Since(start))
   504  			}()
   505  
   506  			done := make(chan error, 1)
   507  			var err error
   508  			go func() {
   509  				switch tc.Operation {
   510  				case "tosci":
   511  					s = operands[0].String()
   512  					// non-extended tests don't retain exponents for 0
   513  					if !tc.Extended && operands[0].IsZero() {
   514  						s = "0"
   515  					}
   516  					// Clear d's bogus data.
   517  					d.Set(operands[0])
   518  					// Set d1 to prevent the result-equals-operand check failing.
   519  					d1 = d
   520  				case "comparetotal":
   521  					var c int
   522  					c = operands[0].CmpTotal(operands[1])
   523  					d.SetInt64(int64(c))
   524  				default:
   525  					var wg sync.WaitGroup
   526  					wg.Add(2)
   527  					// Check that the result is correct even if it is either argument. Use some
   528  					// go routines since we are running tc.Run three times.
   529  					go func() {
   530  						d1 = new(Decimal).Set(operands[0])
   531  						tc.Run(c, done, d1, d1, operands[1])
   532  						wg.Done()
   533  					}()
   534  					go func() {
   535  						if operands[1] != nil {
   536  							d2 = new(Decimal).Set(operands[1])
   537  							tc.Run(c, done, d2, operands[0], d2)
   538  						}
   539  						wg.Done()
   540  					}()
   541  					res, err = tc.Run(c, done, d, operands[0], operands[1])
   542  					wg.Wait()
   543  				}
   544  				done <- nil
   545  			}()
   546  			select {
   547  			case err := <-done:
   548  				if err != nil {
   549  					t.Fatal(err)
   550  				}
   551  			case <-time.After(time.Second * 20):
   552  				t.Fatalf("timeout")
   553  			}
   554  			if d.Coeff.Sign() < 0 {
   555  				t.Fatalf("negative coeff: %s", d.Coeff.String())
   556  			}
   557  			// Make sure the bogus Form above got cleared.
   558  			if d.Form < 0 {
   559  				t.Fatalf("unexpected form: %#v", d)
   560  			}
   561  			// Verify the operands didn't change.
   562  			for i, o := range tc.Operands {
   563  				v := newDecimal(t, opctx, o)
   564  				if v.CmpTotal(operands[i]) != 0 {
   565  					t.Fatalf("operand %d changed from %s to %s", i, o, operands[i])
   566  				}
   567  			}
   568  			// Verify the result-equals-operand worked correctly.
   569  			if d1 != nil && d.CmpTotal(d1) != 0 {
   570  				t.Errorf("first operand as result mismatch: got %s, expected %s", d1, d)
   571  			}
   572  			if d2 != nil && d.CmpTotal(d2) != 0 {
   573  				t.Errorf("second operand as result mismatch: got %s, expected %s", d2, d)
   574  			}
   575  			if !GDAignoreFlags[tc.ID] {
   576  				var rcond Condition
   577  				for _, cond := range tc.Conditions {
   578  					switch cond {
   579  					case "underflow":
   580  						rcond |= Underflow
   581  					case "inexact":
   582  						rcond |= Inexact
   583  					case "overflow":
   584  						rcond |= Overflow
   585  					case "subnormal":
   586  						rcond |= Subnormal
   587  					case "division_undefined":
   588  						rcond |= DivisionUndefined
   589  					case "division_by_zero":
   590  						rcond |= DivisionByZero
   591  					case "division_impossible":
   592  						rcond |= DivisionImpossible
   593  					case "invalid_operation":
   594  						rcond |= InvalidOperation
   595  					case "rounded":
   596  						rcond |= Rounded
   597  					case "clamped":
   598  						rcond |= Clamped
   599  
   600  					case "invalid_context":
   601  						// ignore
   602  
   603  					default:
   604  						t.Fatalf("unknown condition: %s", cond)
   605  					}
   606  				}
   607  
   608  				switch tc.Operation {
   609  				case "tosci":
   610  					// We only care about the operand flags for the string conversion operations.
   611  					res |= opres
   612  				}
   613  
   614  				t.Logf("want flags (%d): %s", rcond, rcond)
   615  				t.Logf("have flags (%d): %s", res, res)
   616  
   617  				// TODO(mjibson): after upscaling, operations need to remove the 0s added
   618  				// after the operation is done. Since this isn't happening, things are being
   619  				// rounded when they shouldn't because the coefficient has so many trailing 0s.
   620  				// Manually remove Rounded flag from context until the TODO is fixed.
   621  				res &= ^Rounded
   622  				rcond &= ^Rounded
   623  
   624  				switch tc.Operation {
   625  				case "log10":
   626  					// TODO(mjibson): Under certain conditions these are exact, but we don't
   627  					// correctly mark them. Ignore these flags for now.
   628  					// squareroot sometimes marks things exact when GDA says they should be
   629  					// inexact.
   630  					rcond &= ^Inexact
   631  					res &= ^Inexact
   632  				}
   633  
   634  				// Don't worry about these flags; they are handled by GoError.
   635  				res &= ^SystemOverflow
   636  				res &= ^SystemUnderflow
   637  
   638  				if (res.Overflow() || res.Underflow()) && (strings.HasPrefix(tc.ID, "rpow") ||
   639  					strings.HasPrefix(tc.ID, "powr")) {
   640  					t.Skip("overflow")
   641  				}
   642  
   643  				// Ignore Clamped on error.
   644  				if tc.Result == "?" {
   645  					rcond &= ^Clamped
   646  					res &= ^Clamped
   647  				}
   648  
   649  				if rcond != res {
   650  					if tc.Operation == "power" && (res.Overflow() || res.Underflow()) {
   651  						t.Skip("power overflow")
   652  					}
   653  					t.Logf("got: %s (%#v)", d, d)
   654  					t.Logf("error: %+v", err)
   655  					t.Errorf("expected flags %q (%d); got flags %q (%d)", rcond, rcond, res, res)
   656  				}
   657  			}
   658  
   659  			if tc.Result == "?" {
   660  				if err != nil {
   661  					return
   662  				}
   663  				t.Fatalf("expected error, got %s", d)
   664  			}
   665  			if err != nil {
   666  				testExponentError(t, err)
   667  				if tc.Operation == "power" && (res.Overflow() || res.Underflow()) {
   668  					t.Skip("power overflow")
   669  				}
   670  				t.Fatalf("%+v", err)
   671  			}
   672  			switch tc.Operation {
   673  			case "tosci", "toeng":
   674  				if strings.HasPrefix(tc.Result, "-NAN") {
   675  					tc.Result = "-NaN"
   676  				}
   677  				if strings.HasPrefix(tc.Result, "-SNAN") {
   678  					tc.Result = "-sNaN"
   679  				}
   680  				if strings.HasPrefix(tc.Result, "NAN") {
   681  					tc.Result = "NaN"
   682  				}
   683  				if strings.HasPrefix(tc.Result, "SNAN") {
   684  					tc.Result = "sNaN"
   685  				}
   686  				expected := tc.Result
   687  				// Adjust 0E- or -0E- tests to match PostgreSQL behavior.
   688  				// See: https://github.com/cockroachdb/cockroach/issues/102217.
   689  				if pos, neg := strings.HasPrefix(expected, "0E-"), strings.HasPrefix(expected, "-0E-"); pos || neg {
   690  					startIdx := 3
   691  					if neg {
   692  						startIdx = 4
   693  					}
   694  					p, err := strconv.ParseInt(expected[startIdx:], 10, 64)
   695  					if err != nil {
   696  						t.Fatalf("unexpected error converting int: %v", err)
   697  					}
   698  					if p <= -lowestZeroNegativeCoefficientCockroach {
   699  						expected = ""
   700  						if neg {
   701  							expected = "-"
   702  						}
   703  						expected += "0." + strings.Repeat("0", int(p))
   704  					}
   705  				}
   706  				if !strings.EqualFold(s, expected) {
   707  					t.Fatalf("expected %s, got %s", expected, s)
   708  				}
   709  				return
   710  			}
   711  			r := newDecimal(t, testCtx, tc.Result)
   712  			var equal bool
   713  			if d.Form == Finite {
   714  				// Don't worry about trailing zeros being inequal in CmpTotal.
   715  				equal = d.Cmp(r) == 0 && d.Negative == r.Negative
   716  			} else {
   717  				equal = d.CmpTotal(r) == 0
   718  			}
   719  			if !equal {
   720  				t.Logf("want: %s", tc.Result)
   721  				t.Logf("got: %s (%#v)", d, d)
   722  				// Some operations allow 1ulp of error in tests.
   723  				switch tc.Operation {
   724  				case "exp", "ln", "log10", "power":
   725  					nc := c.WithPrecision(0)
   726  					nc.Sub(d, d, r)
   727  					if d.Coeff.Cmp(bigOne) == 0 {
   728  						t.Logf("pass: within 1ulp: %s, %s", d, r)
   729  						return
   730  					}
   731  				}
   732  				t.Fatalf("unexpected result")
   733  			} else {
   734  				t.Logf("got: %s (%#v)", d, d)
   735  			}
   736  		})
   737  		if !succeed {
   738  			if *flagFailFast {
   739  				break
   740  			}
   741  		}
   742  	}
   743  }
   744  
   745  func (tc TestCase) PrintIgnore() {
   746  	fmt.Printf("	\"%s\": true,\n", tc.ID)
   747  }
   748  
   749  var GDAignore = map[string]bool{
   750  	// Invalid context
   751  	"expx901":  true,
   752  	"expx902":  true,
   753  	"expx903":  true,
   754  	"expx905":  true,
   755  	"lnx901":   true,
   756  	"lnx902":   true,
   757  	"lnx903":   true,
   758  	"lnx905":   true,
   759  	"logx901":  true,
   760  	"logx902":  true,
   761  	"logx903":  true,
   762  	"logx905":  true,
   763  	"powx4001": true,
   764  	"powx4002": true,
   765  	"powx4003": true,
   766  	"powx4005": true,
   767  
   768  	// NaN payloads with weird digits
   769  	"basx725": true,
   770  	"basx745": true,
   771  
   772  	// NaN payloads
   773  	"cotx970": true,
   774  	"cotx973": true,
   775  	"cotx974": true,
   776  	"cotx977": true,
   777  	"cotx980": true,
   778  	"cotx983": true,
   779  	"cotx984": true,
   780  	"cotx987": true,
   781  	"cotx994": true,
   782  
   783  	// too large exponents, supposed to fail anyway
   784  	"quax525": true,
   785  	"quax531": true,
   786  	"quax805": true,
   787  	"quax806": true,
   788  	"quax807": true,
   789  	"quax808": true,
   790  	"quax809": true,
   791  	"quax810": true,
   792  	"quax811": true,
   793  	"quax812": true,
   794  	"quax813": true,
   795  	"quax814": true,
   796  	"quax815": true,
   797  	"quax816": true,
   798  	"quax817": true,
   799  	"quax818": true,
   800  	"quax819": true,
   801  	"quax820": true,
   802  	"quax821": true,
   803  	"quax822": true,
   804  	"quax861": true,
   805  	"quax862": true,
   806  	"quax866": true,
   807  
   808  	// TODO(mjibson): fix tests below
   809  
   810  	// overflows to infinity
   811  	"powx4125": true,
   812  	"powx4145": true,
   813  
   814  	// exceeds system overflow
   815  	"expx291": true,
   816  	"expx292": true,
   817  	"expx293": true,
   818  	"expx294": true,
   819  	"expx295": true,
   820  	"expx296": true,
   821  
   822  	// inexact zeros
   823  	"addx1633":  true,
   824  	"addx1634":  true,
   825  	"addx1638":  true,
   826  	"addx61633": true,
   827  	"addx61634": true,
   828  	"addx61638": true,
   829  
   830  	// should be -0E-398, got -1E-398
   831  	"addx1613":  true,
   832  	"addx1614":  true,
   833  	"addx1618":  true,
   834  	"addx61613": true,
   835  	"addx61614": true,
   836  	"addx61618": true,
   837  
   838  	// extreme input range, but should work
   839  	"sqtx8636": true,
   840  	"sqtx8644": true,
   841  	"sqtx8646": true,
   842  	"sqtx8647": true,
   843  	"sqtx8648": true,
   844  	"sqtx8650": true,
   845  	"sqtx8651": true,
   846  }
   847  
   848  var GDAignoreFlags = map[string]bool{
   849  	// unflagged clamped
   850  	"sqtx9024": true,
   851  	"sqtx9025": true,
   852  	"sqtx9026": true,
   853  	"sqtx9027": true,
   854  	"sqtx9038": true,
   855  	"sqtx9039": true,
   856  	"sqtx9040": true,
   857  	"sqtx9045": true,
   858  }
   859  

View as plain text