func floatOrDecimalBuiltin2(f func(float64, float64) (Datum, error)) []builtin { return []builtin{ { types: argTypes{floatType, floatType}, returnType: typeFloat, fn: func(_ EvalContext, args DTuple) (Datum, error) { return f(float64(args[0].(DFloat)), float64(args[1].(DFloat))) }, }, { types: argTypes{decimalType, decimalType}, returnType: typeDecimal, fn: func(_ EvalContext, args DTuple) (Datum, error) { v1, _ := args[0].(DDecimal).Float64() v2, _ := args[1].(DDecimal).Float64() r, err := f(v1, v2) if err != nil { return r, err } rf := float64(r.(DFloat)) if math.IsNaN(rf) || math.IsInf(rf, 0) { // TODO(nvanbenschoten) NaN semmantics should be introduced // into the decimal library to support it here. return nil, fmt.Errorf("decimal does not support NaN") } return DDecimal{Decimal: decimal.NewFromFloat(rf)}, nil }, }, } }
func BenchmarkEncodeDecimal(b *testing.B) { rng, _ := randutil.NewPseudoRand() vals := make([]decimal.Decimal, 10000) for i := range vals { vals[i] = decimal.NewFromFloat(rng.Float64()) } buf := make([]byte, 0, 100) b.ResetTimer() for i := 0; i < b.N; i++ { _ = EncodeDecimalAscending(buf, vals[i%len(vals)]) } }
func BenchmarkDecodeDecimal(b *testing.B) { rng, _ := randutil.NewPseudoRand() vals := make([][]byte, 10000) for i := range vals { d := decimal.NewFromFloat(rng.Float64()) vals[i] = EncodeDecimalAscending(nil, d) } buf := make([]byte, 0, 100) b.ResetTimer() for i := 0; i < b.N; i++ { _, _, _ = DecodeDecimalAscending(vals[i%len(vals)], buf) } }
func TestEncodeDecimal(t *testing.T) { testCases := []struct { Value decimal.Decimal Encoding []byte }{ {decimal.NewFromFloat(-math.MaxFloat64), []byte{0x04, 0x64, 0xfc, 0x60, 0x66, 0x44, 0xe4, 0x9e, 0x82, 0xc0, 0x8d, 0x0}}, {decimal.New(-1, 308), []byte{0x04, 0x64, 0xfd, 0x0}}, // Four duplicates to make sure -1*10^4 <= -10*10^3 <= -100*10^2 <= -1*10^4 {decimal.New(-1, 4), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-10, 3), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-100, 2), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-1, 4), []byte{0x0c, 0xfd, 0x0}}, {decimal.New(-9999, 0), []byte{0x0d, 0x38, 0x39, 0x00}}, {decimal.New(-10, 1), []byte{0x0d, 0xfd, 0x00}}, {decimal.New(-99, 0), []byte{0x0e, 0x39, 0x00}}, {decimal.New(-1, 0), []byte{0x0e, 0xfd, 0x0}}, {decimal.New(-123, -5), []byte{0x10, 0x1, 0xe6, 0xc3, 0x0}}, {decimal.New(-1, -307), []byte{0x10, 0x99, 0xeb, 0x0}}, {decimal.NewFromFloat(-math.SmallestNonzeroFloat64), []byte{0x10, 0xa1, 0xf5, 0x0}}, {decimal.New(0, 0), []byte{0x11}}, {decimal.NewFromFloat(math.SmallestNonzeroFloat64), []byte{0x12, 0x5e, 0xa, 0x0}}, {decimal.New(1, -307), []byte{0x12, 0x66, 0x14, 0x0}}, {decimal.New(123, -5), []byte{0x12, 0xfe, 0x19, 0x3c, 0x0}}, {decimal.New(123, -4), []byte{0x13, 0x03, 0x2e, 0x0}}, {decimal.New(123, -3), []byte{0x13, 0x19, 0x3c, 0x0}}, {decimal.New(1, 0), []byte{0x14, 0x02, 0x0}}, {decimal.New(1, 1), []byte{0x14, 0x14, 0x0}}, {decimal.New(12345, -3), []byte{0x14, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(990, -1), []byte{0x14, 0xc6, 0x0}}, {decimal.New(990001, -4), []byte{0x14, 0xc7, 0x01, 0x02, 0x0}}, {decimal.New(9901, -2), []byte{0x14, 0xc7, 0x02, 0x0}}, {decimal.New(10, 1), []byte{0x15, 0x02, 0x0}}, {decimal.New(10001, -2), []byte{0x15, 0x03, 0x01, 0x02, 0x0}}, {decimal.New(1001, -1), []byte{0x15, 0x03, 0x01, 0x14, 0x0}}, {decimal.New(1234, 0), []byte{0x15, 0x19, 0x44, 0x0}}, {decimal.New(12345, -1), []byte{0x15, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(9999, 0), []byte{0x15, 0xc7, 0xc6, 0x0}}, {decimal.New(9999000001, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x02, 0x0}}, {decimal.New(9999000009, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x12, 0x0}}, {decimal.New(9999000010, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0x14, 0x0}}, {decimal.New(9999000090, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0xb4, 0x0}}, {decimal.New(9999000099, -6), []byte{0x15, 0xc7, 0xc7, 0x01, 0x01, 0xc6, 0x0}}, {decimal.New(99990001, -4), []byte{0x15, 0xc7, 0xc7, 0x01, 0x02, 0x0}}, {decimal.New(9999001, -3), []byte{0x15, 0xc7, 0xc7, 0x01, 0x14, 0x0}}, {decimal.New(999901, -2), []byte{0x15, 0xc7, 0xc7, 0x02, 0x0}}, {decimal.New(99991, -1), []byte{0x15, 0xc7, 0xc7, 0x14, 0x0}}, {decimal.New(10000, 0), []byte{0x16, 0x02, 0x0}}, {decimal.New(10001, 0), []byte{0x16, 0x03, 0x01, 0x02, 0x0}}, {decimal.New(12345, 0), []byte{0x16, 0x03, 0x2f, 0x5a, 0x0}}, {decimal.New(123450, 0), []byte{0x16, 0x19, 0x45, 0x64, 0x0}}, {decimal.New(1, 308), []byte{0x1e, 0x9b, 0x2, 0x0}}, {decimal.NewFromFloat(math.MaxFloat64), []byte{0x1e, 0x9b, 0x3, 0x9f, 0x99, 0xbb, 0x1b, 0x61, 0x7d, 0x3f, 0x72, 0x0}}, } var lastEncoded []byte for _, dir := range []Direction{Ascending, Descending} { for i, c := range testCases { var enc []byte var err error var dec decimal.Decimal if dir == Ascending { enc = EncodeDecimalAscending(nil, c.Value) _, dec, err = DecodeDecimalAscending(enc, nil) } else { enc = EncodeDecimalDescending(nil, c.Value) _, dec, err = DecodeDecimalDescending(enc, nil) } if dir == Ascending && !bytes.Equal(enc, c.Encoding) { t.Errorf("unexpected mismatch for %s. expected [% x], got [% x]", c.Value, c.Encoding, enc) } if i > 0 { if (bytes.Compare(lastEncoded, enc) > 0 && dir == Ascending) || (bytes.Compare(lastEncoded, enc) < 0 && dir == Descending) { t.Errorf("%v: expected [% x] to be less than or equal to [% x]", c.Value, testCases[i-1].Encoding, enc) } } if err != nil { t.Error(err) continue } if !dec.Equals(c.Value) { t.Errorf("%d unexpected mismatch for %v. got %v", i, c.Value, dec) } lastEncoded = enc } // Test that appending the decimal to an existing buffer works. var enc []byte var dec decimal.Decimal other := decimal.NewFromFloat(1.23) if dir == Ascending { enc = EncodeDecimalAscending([]byte("hello"), other) _, dec, _ = DecodeDecimalAscending(enc[5:], nil) } else { enc = EncodeDecimalDescending([]byte("hello"), other) _, dec, _ = DecodeDecimalDescending(enc[5:], nil) } if !dec.Equals(other) { t.Errorf("unexpected mismatch for %v. got %v", 1.23, other) } } }