evalengine: new decimal implementation

Signed-off-by: Vicent Marti <vmg@strn.cat>
This commit is contained in:
Vicent Marti 2022-02-18 19:50:15 +01:00
Родитель 9a1adb66bf
Коммит a9e86b0ea3
51 изменённых файлов: 2441 добавлений и 11363 удалений

Просмотреть файл

@ -25,7 +25,7 @@ func (cached *Result) CachedSize(alloc bool) int64 {
}
size := int64(0)
if alloc {
size += int64(96)
size += int64(112)
}
// field Fields []*vitess.io/vitess/go/vt/proto/query.Field
{
@ -48,6 +48,8 @@ func (cached *Result) CachedSize(alloc bool) int64 {
}
// field SessionStateChanges string
size += hack.RuntimeAllocSize(int64(len(cached.SessionStateChanges)))
// field Info string
size += hack.RuntimeAllocSize(int64(len(cached.Info)))
return size
}
func (cached *Value) CachedSize(alloc bool) int64 {

Просмотреть файл

@ -23,7 +23,7 @@ import (
"vitess.io/vitess/go/sqltypes"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal"
"vitess.io/vitess/go/vt/vtgate/evalengine/internal/decimal"
)
// evalengine represents a numeric value extracted from
@ -169,7 +169,7 @@ func addNumericWithError(v1, v2, out *EvalResult) error {
return uintPlusUintWithError(v1.uint64(), v2.uint64(), out)
}
case sqltypes.Decimal:
decimalPlusAny(v1.decimal(), v2, out)
decimalPlusAny(v1.decimal(), v1.length_, v2, out)
return nil
case sqltypes.Float64:
return floatPlusAny(v1.float64(), v2, out)
@ -190,7 +190,7 @@ func subtractNumericWithError(v1, v2, out *EvalResult) error {
case sqltypes.Float64:
return anyMinusFloat(v1, v2.float64(), out)
case sqltypes.Decimal:
anyMinusDecimal(v1, v2.decimal(), out)
anyMinusDecimal(v1, v2.decimal(), v2.length_, out)
return nil
}
case sqltypes.Uint64:
@ -202,7 +202,7 @@ func subtractNumericWithError(v1, v2, out *EvalResult) error {
case sqltypes.Float64:
return anyMinusFloat(v1, v2.float64(), out)
case sqltypes.Decimal:
anyMinusDecimal(v1, v2.decimal(), out)
anyMinusDecimal(v1, v2.decimal(), v2.length_, out)
return nil
}
case sqltypes.Float64:
@ -212,7 +212,7 @@ func subtractNumericWithError(v1, v2, out *EvalResult) error {
case sqltypes.Float64:
return anyMinusFloat(v1, v2.float64(), out)
default:
decimalMinusAny(v1.decimal(), v2, out)
decimalMinusAny(v1.decimal(), v1.length_, v2, out)
return nil
}
}
@ -234,7 +234,7 @@ func multiplyNumericWithError(v1, v2, out *EvalResult) error {
case sqltypes.Float64:
return floatTimesAny(v1.float64(), v2, out)
case sqltypes.Decimal:
decimalTimesAny(v1.decimal(), v2, out)
decimalTimesAny(v1.decimal(), v1.length_, v2, out)
return nil
}
return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid arithmetic between: %s %s", v1.value().String(), v2.value().String())
@ -266,7 +266,8 @@ func divideNumericWithError(v1, v2 *EvalResult, precise bool, out *EvalResult) e
}
return floatDivideAnyWithError(v1f, v2, out)
default:
return decimalDivide(v1, v2, divPrecisionIncrement, out)
decimalDivide(v1, v2, divPrecisionIncrement, out)
return nil
}
}
@ -427,104 +428,43 @@ func floatTimesAny(v1 float64, v2 *EvalResult, out *EvalResult) error {
return nil
}
const roundingModeArithmetic = decimal.ToZero
const roundingModeFormat = decimal.ToNearestAway
const roundingModeIntegerConversion = decimal.ToNearestAway
var decimalContextSQL = decimal.Context{
MaxScale: 30,
MinScale: 0,
Precision: 65,
Traps: ^(decimal.Inexact | decimal.Rounded | decimal.Subnormal),
RoundingMode: roundingModeArithmetic,
OperatingMode: decimal.GDA,
}
func newDecimalUint64(x uint64) *decimalResult {
var result decimalResult
result.num.Context = decimalContextSQL
result.num.SetUint64(x)
return &result
}
func newDecimalString(x string) (*decimalResult, error) {
var result decimalResult
result.num.Context = decimalContextSQL
result.num.SetString(x)
if result.num.Context.Conditions != 0 {
return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", result.num.Context.Conditions)
func maxprec(a, b int32) int32 {
if a > b {
return a
}
result.frac = result.num.Scale()
return &result, nil
return b
}
func newDecimalInt64(x int64) *decimalResult {
var result decimalResult
result.num.Context = decimalContextSQL
result.num.SetMantScale(x, 0)
return &result
}
func newDecimalFloat64(f float64) *decimalResult {
var result decimalResult
result.num.Context = decimalContextSQL
result.num.SetFloat64(f)
result.frac = result.num.Scale()
return &result
}
func newDecimalFromOp(left, right *decimalResult, op func(r, x, y *decimal.Big)) *decimalResult {
var result decimalResult
result.num.Context = decimalContextSQL
op(&result.num, &left.num, &right.num)
if left.frac > right.frac {
result.frac = left.frac
} else {
result.frac = right.frac
}
return &result
}
func decimalPlusAny(v1 *decimalResult, v2 *EvalResult, out *EvalResult) {
func decimalPlusAny(v1 decimal.Decimal, f1 int32, v2 *EvalResult, out *EvalResult) {
v2d := v2.coerceToDecimal()
result := newDecimalFromOp(v1, v2d, func(r, x, y *decimal.Big) { r.Add(x, y) })
out.setDecimal(result)
out.setDecimal(v1.Add(v2d), maxprec(f1, v2.length_))
}
func decimalMinusAny(v1 *decimalResult, v2 *EvalResult, out *EvalResult) {
func decimalMinusAny(v1 decimal.Decimal, f1 int32, v2 *EvalResult, out *EvalResult) {
v2d := v2.coerceToDecimal()
result := newDecimalFromOp(v1, v2d, func(r, x, y *decimal.Big) { r.Sub(x, y) })
out.setDecimal(result)
out.setDecimal(v1.Sub(v2d), maxprec(f1, v2.length_))
}
func anyMinusDecimal(v1 *EvalResult, v2 *decimalResult, out *EvalResult) {
func anyMinusDecimal(v1 *EvalResult, v2 decimal.Decimal, f2 int32, out *EvalResult) {
v1d := v1.coerceToDecimal()
result := newDecimalFromOp(v1d, v2, func(r, x, y *decimal.Big) { r.Sub(x, y) })
out.setDecimal(result)
out.setDecimal(v1d.Sub(v2), maxprec(v1.length_, f2))
}
func decimalTimesAny(v1 *decimalResult, v2 *EvalResult, out *EvalResult) {
func decimalTimesAny(v1 decimal.Decimal, f1 int32, v2 *EvalResult, out *EvalResult) {
v2d := v2.coerceToDecimal()
result := newDecimalFromOp(v1, v2d, func(r, x, y *decimal.Big) { r.Mul(x, y) })
out.setDecimal(result)
out.setDecimal(v1.Mul(v2d), maxprec(f1, v2.length_))
}
const divPrecisionIncrement = 4
func decimalDivide(v1, v2 *EvalResult, incrPrecision int, out *EvalResult) error {
left := v1.coerceToDecimal()
right := v2.coerceToDecimal()
var result decimalResult
result.num.Context = decimalContextSQL
result.frac = left.frac + incrPrecision
result.num.Div(&left.num, &right.num, incrPrecision)
if result.num.Context.Conditions&(decimal.DivisionByZero|decimal.DivisionUndefined) != 0 {
func decimalDivide(v1, v2 *EvalResult, incrPrecision int32, out *EvalResult) {
v1d := v1.coerceToDecimal()
v2d := v2.coerceToDecimal()
if v2d.IsZero() {
out.setNull()
return nil
return
}
out.setDecimal(&result)
return nil
out.setDecimal(v1d.Div(v2d, incrPrecision), v1.length_+incrPrecision)
}
func floatDivideAnyWithError(v1 float64, v2 *EvalResult, out *EvalResult) error {

Просмотреть файл

@ -1,422 +0,0 @@
/*
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by Sizegen. DO NOT EDIT.
package evalengine
import (
"math"
"reflect"
"unsafe"
hack "vitess.io/vitess/go/hack"
)
type cachedObject interface {
CachedSize(alloc bool) int64
}
func (cached *ArithmeticExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field Op vitess.io/vitess/go/vt/vtgate/evalengine.ArithmeticOp
if cc, ok := cached.Op.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *BinaryExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(32)
}
// field Left vitess.io/vitess/go/vt/vtgate/evalengine.Expr
if cc, ok := cached.Left.(cachedObject); ok {
size += cc.CachedSize(true)
}
// field Right vitess.io/vitess/go/vt/vtgate/evalengine.Expr
if cc, ok := cached.Right.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *BindVariable) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(24)
}
// field Key string
size += hack.RuntimeAllocSize(int64(len(cached.Key)))
return size
}
func (cached *BitwiseExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field Op vitess.io/vitess/go/vt/vtgate/evalengine.BitwiseOp
if cc, ok := cached.Op.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *BitwiseNotExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(16)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *CallExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(80)
}
// field Arguments vitess.io/vitess/go/vt/vtgate/evalengine.TupleExpr
{
size += hack.RuntimeAllocSize(int64(cap(cached.Arguments)) * int64(16))
for _, elem := range cached.Arguments {
if cc, ok := elem.(cachedObject); ok {
size += cc.CachedSize(true)
}
}
}
// field Aliases []vitess.io/vitess/go/vt/sqlparser.ColIdent
{
size += hack.RuntimeAllocSize(int64(cap(cached.Aliases)) * int64(40))
for _, elem := range cached.Aliases {
size += elem.CachedSize(false)
}
}
// field Method string
size += hack.RuntimeAllocSize(int64(len(cached.Method)))
// field F vitess.io/vitess/go/vt/vtgate/evalengine.builtin
if cc, ok := cached.F.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *CollateExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(24)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *Column) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(16)
}
return size
}
func (cached *ComparisonExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field Op vitess.io/vitess/go/vt/vtgate/evalengine.ComparisonOp
if cc, ok := cached.Op.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *ConvertExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(64)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
// field Type string
size += hack.RuntimeAllocSize(int64(len(cached.Type)))
return size
}
func (cached *ConvertUsingExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(24)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *EvalResult) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(80)
}
// field expr vitess.io/vitess/go/vt/vtgate/evalengine.Expr
if cc, ok := cached.expr.(cachedObject); ok {
size += cc.CachedSize(true)
}
// field env *vitess.io/vitess/go/vt/vtgate/evalengine.ExpressionEnv
size += cached.env.CachedSize(true)
// field bytes_ []byte
{
size += hack.RuntimeAllocSize(int64(cap(cached.bytes_)))
}
// field tuple_ *[]vitess.io/vitess/go/vt/vtgate/evalengine.EvalResult
if cached.tuple_ != nil {
size += int64(24)
size += hack.RuntimeAllocSize(int64(cap(*cached.tuple_)) * int64(80))
for _, elem := range *cached.tuple_ {
size += elem.CachedSize(false)
}
}
// field decimal_ *vitess.io/vitess/go/vt/vtgate/evalengine.decimalResult
size += cached.decimal_.CachedSize(true)
return size
}
//go:nocheckptr
func (cached *ExpressionEnv) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field BindVars map[string]*vitess.io/vitess/go/vt/proto/query.BindVariable
if cached.BindVars != nil {
size += int64(48)
hmap := reflect.ValueOf(cached.BindVars)
numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9)))))))
numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10))))
size += hack.RuntimeAllocSize(int64(numOldBuckets * 208))
if len(cached.BindVars) > 0 || numBuckets > 1 {
size += hack.RuntimeAllocSize(int64(numBuckets * 208))
}
for k, v := range cached.BindVars {
size += hack.RuntimeAllocSize(int64(len(k)))
size += v.CachedSize(true)
}
}
// field Row []vitess.io/vitess/go/sqltypes.Value
{
size += hack.RuntimeAllocSize(int64(cap(cached.Row)) * int64(32))
for _, elem := range cached.Row {
size += elem.CachedSize(false)
}
}
return size
}
//go:nocheckptr
func (cached *InExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field Hashed map[uintptr]int
if cached.Hashed != nil {
size += int64(48)
hmap := reflect.ValueOf(cached.Hashed)
numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9)))))))
numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10))))
size += hack.RuntimeAllocSize(int64(numOldBuckets * 144))
if len(cached.Hashed) > 0 || numBuckets > 1 {
size += hack.RuntimeAllocSize(int64(numBuckets * 144))
}
}
return size
}
func (cached *IsExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(32)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *LikeExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(64)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field Match vitess.io/vitess/go/mysql/collations.WildcardPattern
if cc, ok := cached.Match.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *Literal) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(80)
}
// field Val vitess.io/vitess/go/vt/vtgate/evalengine.EvalResult
size += cached.Val.CachedSize(false)
return size
}
func (cached *LogicalExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(64)
}
// field BinaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
size += cached.BinaryExpr.CachedSize(false)
// field opname string
size += hack.RuntimeAllocSize(int64(len(cached.opname)))
return size
}
func (cached *NegateExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(16)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *NotExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(16)
}
// field UnaryExpr vitess.io/vitess/go/vt/vtgate/evalengine.UnaryExpr
size += cached.UnaryExpr.CachedSize(false)
return size
}
func (cached *UnaryExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(16)
}
// field Inner vitess.io/vitess/go/vt/vtgate/evalengine.Expr
if cc, ok := cached.Inner.(cachedObject); ok {
size += cc.CachedSize(true)
}
return size
}
func (cached *WeightStringCallExpr) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(48)
}
// field String vitess.io/vitess/go/vt/vtgate/evalengine.Expr
if cc, ok := cached.String.(cachedObject); ok {
size += cc.CachedSize(true)
}
// field Cast string
size += hack.RuntimeAllocSize(int64(len(cached.Cast)))
return size
}
func (cached *builtinMultiComparison) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(24)
}
// field name string
size += hack.RuntimeAllocSize(int64(len(cached.name)))
return size
}
func (cached *decimalResult) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(112)
}
// field num vitess.io/vitess/go/vt/vtgate/evalengine/decimal.Big
size += cached.num.CachedSize(false)
return size
}

Просмотреть файл

@ -77,7 +77,7 @@ func (c *ConvertExpr) eval(env *ExpressionEnv, result *EvalResult) {
if c.HasScale {
d = c.Scale
}
result.makeDecimal(m, d)
result.makeDecimal(int32(m), int32(d))
case "DOUBLE", "REAL":
result.makeFloat()
case "FLOAT":

58
go/vt/vtgate/evalengine/decimal/.gitignore поставляемый
Просмотреть файл

@ -1,58 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
# Artifacts, profiles, etc.
*.exe
*.test
*.prof
*.tags
.git
*.swp
*.txt
*.tags
tags
*.test
*.out
*.zip
*.gz
# Internal
literature/
j/
# Fuzzing
fuzz/**/corpus
fuzz/**/crashers
fuzz/**/suppressions
# Benchmark artifacts
benchmarks/decbench
*.xml
*.class
.idea/
benchmarks/regen.go
# Development
internal/nat/
# Testing
x.bash
*.decTest

Просмотреть файл

@ -1,11 +0,0 @@
# This is the official list of 'decimal' authors for copyright purposes.
# Names should be added to this file as one of
# Organization's name
# Individual's name <submission email address>
Eric Lagergen <eric@ericlagergren.com>
Nathan Hack
fantomgs
Timothy Ham
Richard Dingwall <rdingwall@gmail.com>

Просмотреть файл

@ -1,27 +0,0 @@
Copyright (c) 2016, Eric Lagergren
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.

Просмотреть файл

@ -1,44 +0,0 @@
# decimal [![Build Status](https://travis-ci.org/ericlagergren/decimal.png?branch=master)](https://travis-ci.org/ericlagergren/decimal) [![GoDoc](https://godoc.org/github.com/ericlagergren/decimal?status.svg)](https://godoc.org/github.com/ericlagergren/decimal)
`decimal` implements arbitrary precision, decimal floating-point numbers, per
the [General Decimal Arithmetic](http://speleotrove.com/decimal/) specification.
## Features
* Useful zero values.
The zero value of a `decimal.Big` is 0, just like `math/big`.
* Multiple operating modes.
Different operating modes allow you to tailor the package's behavior to your
needs. The GDA mode strictly implements the GDA specification, while the Go
mode implements familiar Go idioms.
* High performance.
`decimal` is consistently one of the fastest arbitrary-precision decimal
floating-point libraries, regardless of language.
* An extensive math library.
The `math/` subpackage implements elementary and trigonometric functions,
continued fractions, and more.
* A familiar, idiomatic API.
`decimal`'s API follows `math/big`'s API, so there isn't a steep learning
curve.
## Installation
`go get github.com/ericlagergren/decimal`
## Documentation
[GoDoc](http://godoc.org/github.com/ericlagergren/decimal)
## Versioning
`decimal` uses Semantic Versioning. The current version is 3.3.1.
`decimal` only explicitly supports the two most recent major Go 1.X versions.
## License
[BSD 3-clause](https://github.com/ericlagergren/decimal/blob/master/LICENSE)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,461 +0,0 @@
package decimal
import (
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/arith"
)
// The binary splitting algorithm is made of four functions
// a(n), b(n), q(n), and p(n)
//
// a(n)p(0)...p(n)
// S = sum_(n=0)^infinity -----------------
// b(n)q(0)...q(n)
//
// We split it up into [n1, n2) slices and calculate using the following
//
// B = b(n1)...b(n2-1)
// P = p(n1)...p(n2-1)
// Q = q(n1)...q(n2-1)
//
// then assign
//
// T = BQS
//
// to solve for S
//
// S = T/BQ
//
// ----
//
// The "trick" is that we plan to "binary-ly" split up the the range [0, n) such
// that for a given range [n1, n2) we will split it into two smaller ranges
// [n1, m) and [m, n2) where m = floor((n1+n2)/2). When n2 - n1 is either
// 1, 2, 3, or 4 we plan to calculate manually, but for anything else larger
// we then define a given range [n1, n2), split it into [n1, m) and [m, n2),
// noting a "Left" and "Right" side. Then, for each side with a B, P, Q, and T
// we'll note the subscript via a L or R. We then have the following formulation:
//
// B = B_l*B_r
// P = P_l*P_r
// Q = Q_l*Q_r
// T = B_l*P_l*T_r + B_r*Q_r*T_l
//
// (Take care in noticing Q_l and P_r aren't used in calculating T.)
// Then solve for S the same as above S = T/BQ.
// apbqBinarySplitState is used to hold intermediate values for each step in the
// calculation.
type apbqBinarySplitState struct {
B *Big
P *Big
Q *Big
T *Big
}
func newState() *apbqBinarySplitState {
return &apbqBinarySplitState{
B: new(Big),
P: new(Big),
Q: new(Big),
T: new(Big),
}
}
func (s *apbqBinarySplitState) term(z *Big, ctx Context) *Big {
return ctx.Quo(z, s.T, ctx.Mul(z, s.B, s.Q)) // z = T / B*Q
}
// SplitFunc returns the intermediate value for a given n. The returned decimal
// must not be modified by the caller and may only be valid until the next
// invocation of said function. This allows the implementation to conserve
// memory usage.
type SplitFunc func(n uint64) *Big
// BinarySplit sets z to the result of the binary splitting formula and returns
// z. The formula is defined as:
//
// ∞ a(n)p(0) ... p(n)
// S = Σ -------------------
// n=0 b(n)q(0) ... q(n)
//
// It should only be used when the number of terms is known ahead of time. If
// start is not in [start, stop) or stop is not in (start, stop], BinarySplit
// will panic.
func BinarySplit(z *Big, ctx Context, start, stop uint64, A, P, B, Q SplitFunc) *Big {
switch {
case stop == start:
panic("math: the start and stop of BinarySplit cannot be not be the same")
case stop < start:
panic("math: the stop of BinarySplit must be larger than the start")
}
state := newState()
state.calculate(ctx, start, stop, A, P, B, Q)
return state.term(z, ctx)
}
// BinarySplitDynamic sets z to the result of the binary splitting formula. It
// should be used when the number of terms is not known ahead of time. For more
// information, See BinarySplit.
func BinarySplitDynamic(ctx Context, A, P, B, Q SplitFunc) *Big {
// TODO(eric): get a handle on this function's memory usage.
// BinarySplitDynamic does not take a receiver since binary splitting uses
// too much memory for a receiver to be useful. It's also difficult to keep
// track of the receiver.
// For this algorithm we start with a standard 16 terms to mark the first
// return value's status, then we calculate the next 4 terms and mark the
// difference (if ZERO return as is else repeat +4 terms until at least
// 1 digit of ctx is gained). We then use that to linearly determine the
// "last term," then repeat when calculating each of the parts the following
// will be used:
//
// B = B_l*B_r
// P = P_l*P_r
// Q = Q_l*Q_r
// T = B_l*P_l*T_r + B_r*Q_r*T_l
currentLastTerm := uint64(16)
current := newState()
current.calculate(ctx, 0, currentLastTerm, A, P, B, Q)
// the marked value is what should be returned which is T/(BQ)
markValue1 := current.term(new(Big), ctx)
markValue2 := new(Big)
diff := new(Big) // markValue1 - markValue2
// now get the next marked value, if the difference isn't already ZERO we need
// at least one digit of ctx to continue
nextLastTerm := currentLastTerm
next := &apbqBinarySplitState{
B: new(Big).Copy(current.B),
P: new(Big).Copy(current.P),
Q: new(Big).Copy(current.Q),
T: new(Big).Copy(current.T),
}
var expectedLastTerm uint64
deltaTerm := uint64(4)
eps := New(1, ctx.Precision)
tmp := newState()
for {
for {
tmp.calculate(ctx, nextLastTerm, nextLastTerm+deltaTerm, A, P, B, Q)
next.combine(ctx, next, tmp)
nextLastTerm += deltaTerm
// markValue2 = T / (B * Q)
next.term(markValue2, ctx)
// Terms have converged.
if markValue1.Cmp(markValue2) == 0 {
return markValue2
}
// if not equal one of two things could be happening
// 1) markValue2 approaching a value away from markValue1 (something
// not close to markValue1)
// 2) markValue2 approaching a value toward markValue1 (something
// close to markValue1)
//
// in the 1) case precision should stay the same but scale will change
// in the 2) case scale & precision should stay the same but the difference
// should see a reduction is the precision
// we'll check for the first case since it doesn't require any "real"
// calculations
if markValue1.Scale() != markValue2.Scale() {
// there was a change so save the current state
current = next
// next calculate the expectedLastTerm and add 4 to ensure it is always >0
scaleDiff := arith.Abs(int64(markValue1.Scale()) - int64(markValue2.Scale()))
expectedLastTerm = nextLastTerm + uint64(float64(nextLastTerm-currentLastTerm)*float64(ctx.Precision)/float64(scaleDiff)) + 4
currentLastTerm = nextLastTerm
break
}
// if not equal take the difference and figure out if we
// have at least one digit of ctx gained
ctx.Sub(diff, markValue1, markValue2)
// here's the one case where we need to do a check for
// something 1E-ctx if equal to or less than
if diff.CmpAbs(eps) < 0 {
return markValue2
}
// we want to have at least 1 digit which really means we
// need a change in ctx of diff of 2 or greater
precChange := arith.Abs(int64(markValue1.Precision()) - int64(diff.Precision()))
if precChange > 1 {
// we have something that we can use to
// calculate the true expected last term
// combine the currentState with this additional state
// update the currentLastTerm and then calculate expectedLastTerm
current = next
// we'll calculate expectedLastTerm but also add 4 to ensure it is always >0
expectedLastTerm = nextLastTerm + uint64(float64(nextLastTerm-currentLastTerm)*float64(ctx.Precision)/float64(precChange)) + 4
currentLastTerm = nextLastTerm
break
}
// if for some reason we haven't seen the expected change
// it could be because the markValue1 and markValue2 are extremely different
// so we'll breakout and hope the next iteration is better
// worse case it's not and these continues until the value converges
// in which case markValue1 and markValue2 will at some point be equal
if nextLastTerm-currentLastTerm > 16 {
// save the current state
current = next
// and set the expected and current to nextLastTerm
expectedLastTerm = nextLastTerm
currentLastTerm = nextLastTerm
break
}
}
// now we have what we expect to be way closer to the true n
if currentLastTerm != expectedLastTerm {
tmp.calculate(ctx, currentLastTerm, expectedLastTerm, A, P, B, Q)
current.combine(ctx, current, tmp)
}
current.term(markValue1, ctx)
currentLastTerm = expectedLastTerm
nextLastTerm = currentLastTerm
next = current
}
}
func (s *apbqBinarySplitState) calculate(ctx Context, start, end uint64, A, P, B, Q SplitFunc) {
switch n1 := start; end - start {
case 1:
s.B.Copy(B(n1))
s.P.Copy(P(n1))
s.Q.Copy(Q(n1))
ctx.Mul(s.T, A(n1), s.P /* P1 */)
case 2:
n2 := n1 + 1
// B = B1 * B2
// P = P1 * P2
// Q = Q1 * Q2
// T =
// t0 = (A1 * * B2 * P1 * Q2) +
// t1 = (A2 * B1 * * P1 * P2 * )
s.P.Copy(P(n1))
s.B.Copy(B(n2))
s.Q.Copy(Q(n2))
// T = A1*P1*B2*Q2 + B1*A2*P12
// Compute the first half of T.
ctx.Mul(s.T, A(n1), s.P /* P1 */)
ctx.Mul(s.T, s.T, s.B /* B2 */)
ctx.Mul(s.T, s.T, s.Q /* Q2 */)
// We no longer need Q, so compute Q.
ctx.Mul(s.Q, s.Q, Q(n1))
// We no longer need B2, so grab B1 and then compute B.
B1 := B(n1)
ctx.Mul(s.B, s.B, B1)
// We no longer need P1 or P2, so calculate P12 which is needed for the
// second half of T.
ctx.Mul(s.P, s.P, P(n2))
// Finish computing T.
t1 := new(Big)
ctx.Mul(t1, B1, A(n2))
ctx.FMA(s.T, t1, s.P /* P12 */, s.T) // combine the final multiply with t0 + t1
case 3:
n2 := n1 + 1
n3 := n2 + 1
// B = B1 * B2 * B3
// P = P1 * P2 * P3
// Q = Q1 * Q2 * Q3
// T =
// t0 = (A1 * B2 * B3 * P1 * __ * Q2 * Q3) +
// t1 = (A2 * B1 * B3 * P1 * P2 * Q3) +
// t2 = (A3 * B1 * B2 * P1 * P2 * P3 )
// A{1,2,3} are transient, so we don't need to store them.
A1 := A(n1)
s.P.Copy(P(n1)) // P = P1
// T_0 = A1 * __ * __ * P1 * __ * __
// B2 B3 Q2 Q3
ctx.Mul(s.T, A1, s.P)
// P = P1 * P2 since we need it for t1.
ctx.Mul(s.P, s.P, P(n2))
t1 := new(Big)
// T_1 = A2 * __ * __ * P1 * P2 * __
// B1 B3 Q3
ctx.Mul(t1, A(n2), s.P)
// P = P1 * P2 * P3; P is finished.
ctx.Mul(s.P, s.P, P(n3))
t2 := new(Big)
// T_2 = A3 * __ * __ * P1 * P2 * P3
// B1 B2
ctx.Mul(t2, A(n3), s.P)
B1 := B(n1)
s.B.Copy(B1)
// T_1 = A2 * B1 * __ * P1 * P2 * __
// B3 Q3
ctx.Mul(t1, t1, s.B /* B1 */)
B2 := B(n2)
// T_0 = A1 * B2 * __ * P1 * __ * __
// B3 Q2 Q3
ctx.Mul(s.T, s.T, B2)
// B = B1 * B2
ctx.Mul(s.B, s.B, B(n2))
// T_2 = A3 * B1 * B2 * P1 * P2 * P3; T_2 is finished.
ctx.Mul(t2, t2, s.B /* B12 */)
// T_1 = A2 * B1 * __ P1 * P2 * __
// B3 Q3
ctx.Mul(t1, t1, B2)
B3 := B(n3)
// T_0 = A1 * B2 * B3 * P1 * __ * __
// Q2 Q3
ctx.Mul(s.T, s.T, B3)
// T_1 = A3 * B1 * B3 * P1 * P2 * P3 * __
// Q3
ctx.Mul(t1, t1, B3)
// B = B1 * B2 * B3; B is finished.
ctx.Mul(s.B, s.B, B3)
// Q = Q3.
s.Q.Copy(Q(n3))
// T_1 = A2 * B1 * B3 * P1 * P2 * Q3; T_1 is finished.
ctx.Mul(t1, t1, s.Q)
// Q = Q2 * Q3.
ctx.Mul(s.Q, s.Q, Q(n2))
// T_0 = A1 * B2 * B3 * P1 * Q2 * Q3; T_0 is finished.
ctx.Mul(s.T, s.T, s.Q)
// Q = Q1 * Q2 * Q3; Q is finished.
ctx.Mul(s.Q, s.Q, Q(n1))
// T = T_0 + T_1 + T_2; T is finsihed.
ctx.Add(s.T, s.T, t1)
ctx.Add(s.T, s.T, t2)
case 4:
n2 := n1 + 1
n3 := n2 + 1
n4 := n3 + 1
// B = B1 * B2 * B3 * B3
// P = P1 * P2 * P3 * P4
// Q = Q1 * Q2 * Q3 * Q3
// T =
// t0 = (A1 * P1 * B2 * B3 * B4 * __ * Q2 * Q3 * Q4) +
// t1 = (A2 * P1 * P2 * B1 * B3 * B4 * Q3 * Q4) +
// t2 = (A3 * P1 * P2 * P3 * B1 * B2 * B4 * Q4) +
// t3 = (A4 * P1 * P2 * P3 * P4 * B1 * B2 * B3 )
// A{1,2,3,4} are transient, so we don't need to store them.
t1 := new(Big)
t2 := new(Big)
t3 := new(Big).Copy(A(n4)) // T_3 needs: P1234, B123
s.Q.Copy(Q(n4)) // Q = Q4.
ctx.Mul(t2, A(n3), s.Q) // T_2 needs: P123, B124.
ctx.Mul(s.Q, s.Q, Q(n3)) // Q = Q34.
ctx.Mul(t1, A(n2), s.Q) // T_1 needs: P12, B134.
ctx.Mul(s.Q, s.Q, Q(n2)) // Q = Q234.
ctx.Mul(s.T, A(n1), s.Q) // T_0 needs: P1, B234.
ctx.Mul(s.Q, s.Q, Q(n1)) // Q = Q1234; Q is finished.
s.P.Copy(P(n1)) // P = P1.
ctx.Mul(s.T, s.T, s.P) // T_0 needs: B234.
ctx.Mul(s.P, s.P, P(n2)) // P = P12.
ctx.Mul(t1, t1, s.P) // T_1 needs: B134.
ctx.Mul(s.P, s.P, P(n3)) // P = P123.
ctx.Mul(t2, t2, s.P) // T_2 needs: B12.
ctx.Mul(s.P, s.P, P(n4)) // P = P1234; P is finished.
ctx.Mul(t3, t3, s.P) // T_3 needs: B123.
b1 := new(Big).Copy(B(n1))
ctx.Mul(t3, t3, b1) // T_3 needs: B23.
ctx.Mul(t2, t2, b1) // T_2 needs: B2.
ctx.Mul(t1, t1, b1) // T_1 is finished.
s.B.Copy(B(n2)) // B = B2.
ctx.Mul(t2, t2, s.B /* B2 */) // T_2 is finished.
ctx.Mul(s.B, s.B, B(n3)) // B = B23.
ctx.Mul(t3, t3, s.B /* B23 */) // T_3 is finished.
ctx.Mul(s.T, s.T, s.B) // T_0 is finished.
ctx.Mul(s.B, s.B, b1) // B = B123.
ctx.Mul(s.B, s.B, B(n4)) // B = B1234.
ctx.Add(s.T, s.T, t1) // T = T_0 + T_1
ctx.Add(s.T, s.T, t2) // T = T_0 + T_1 + T_2
ctx.Add(s.T, s.T, t3) // T = T_0 + T_1 + T_2 + T_3
default:
// here we have something bigger so we'll do a binary split
// first find the mid point between the points and create the two side
// then do the calculations and return the value
m := uint64((start + end) / 2)
// We can reuse s as one of the states.
s.calculate(ctx, start, m, A, P, B, Q)
r := newState()
r.calculate(ctx, m, end, A, P, B, Q)
// Generically, the following is done
//
// B = B_l*B_r
// P = P_l*P_r
// Q = Q_l*Q_r
// T = B_l*P_l*T_r + B_r*Q_r*T_l
//
s.combine(ctx, s, r)
}
}
// combine computes the following:
//
// B = B_l*B_r
// P = P_l*P_r
// Q = Q_l*Q_r
// T = B_l*P_l*T_r + B_r*Q_r*T_l
//
func (s *apbqBinarySplitState) combine(ctx Context, L, R *apbqBinarySplitState) {
// T = L.B*L.P*R.T, t1 = R.B*R.Q*L.T
t0 := getDec(ctx)
ctx.Mul(t0, L.B, L.P)
ctx.Mul(t0, t0, R.T)
t1 := getDec(ctx)
ctx.Mul(t1, R.B, R.Q)
ctx.FMA(s.T, t1, L.T, t0) // combine the final multiply and t0 + t1
ctx.Mul(s.B, L.B, R.B)
ctx.Mul(s.P, L.P, R.P)
ctx.Mul(s.Q, L.Q, R.Q)
putDec(t0)
putDec(t1)
}

Просмотреть файл

@ -1,29 +0,0 @@
/*
Copyright 2021 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by Sizegen. DO NOT EDIT.
package decimal
func (cached *Big) CachedSize(alloc bool) int64 {
if cached == nil {
return int64(0)
}
size := int64(0)
if alloc {
size += int64(112)
}
return size
}

Просмотреть файл

@ -1,66 +0,0 @@
package decimal
import (
"sync"
)
type lazyDecimal struct {
once sync.Once
s string
x *Big
}
func (l *lazyDecimal) get() *Big {
l.once.Do(func() {
l.x, _ = new(Big).SetString(l.s)
})
return l.x
}
func newLazyDecimal(s string) *lazyDecimal {
return &lazyDecimal{s: s}
}
const (
// constPrec is the precision for mathematical constants like
// e, pi, etc.
constPrec = 300
defaultExtraPrecision = 3
)
var (
_E = newLazyDecimal("2.71828182845904523536028747135266249775724709369995957496696762772407663035354759457138217852516642742746639193200305992181741359662904357290033429526059563073813232862794349076323382988075319525101901157383418793070215408914993488416750924476146066808226480016847741185374234544243710753907774499207")
_Ln10 = newLazyDecimal("2.30258509299404568401799145468436420760110148862877297603332790096757260967735248023599720508959829834196778404228624863340952546508280675666628736909878168948290720832555468084379989482623319852839350530896537773262884616336622228769821988674654366747440424327436515504893431493939147961940440022211")
_Pi = newLazyDecimal("3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127")
_Pi2 = newLazyDecimal("1.57079632679489661923132169163975144209858469968755291048747229615390820314310449931401741267105853399107404325664115332354692230477529111586267970406424055872514205135096926055277982231147447746519098221440548783296672306423782411689339158263560095457282428346173017430522716332410669680363012457064")
_Sqrt3 = newLazyDecimal("1.73205080756887729352744634150587236694280525381038062805580697945193301690880003708114618675724857567562614141540670302996994509499895247881165551209437364852809323190230558206797482010108467492326501531234326690332288665067225466892183797122704713166036786158801904998653737985938946765034750657605")
approx1 = newLazyDecimal("0.259")
approx2 = newLazyDecimal("0.819")
approx3 = newLazyDecimal("0.0819")
approx4 = newLazyDecimal("2.59")
ptFive = newLazyDecimal("0.5")
_10005 = newLazyDecimal("10005")
_426880 = newLazyDecimal("426880")
_13591409 = newLazyDecimal("13591409")
_545140134 = newLazyDecimal("545140134")
_10939058860032000 = newLazyDecimal("10939058860032000")
negFour = newLazyDecimal("-4")
negOne = newLazyDecimal("-1")
one = newLazyDecimal("1")
onePtFour = newLazyDecimal("1.4")
two = newLazyDecimal("2")
three = newLazyDecimal("3")
four = newLazyDecimal("4")
five = newLazyDecimal("5")
six = newLazyDecimal("6")
eight = newLazyDecimal("8")
ten = newLazyDecimal("10")
eleven = newLazyDecimal("11")
sixteen = newLazyDecimal("16")
eighteen = newLazyDecimal("18")
thirtyTwo = newLazyDecimal("32")
eightyOne = newLazyDecimal("81")
)

Просмотреть файл

@ -1,365 +0,0 @@
package decimal
import (
"fmt"
"strings"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/c"
)
// Precision and scale limits.
const (
MaxScale = c.MaxScale // largest allowed scale.
MinScale = -MaxScale // smallest allowed scale.
MaxPrecision = MaxScale // largest allowed Context precision.
MinPrecision = 1 // smallest allowed Context precision.
UnlimitedPrecision = MaxPrecision + 1 // no precision, but may error.
DefaultPrecision = 16 // default precision for literals.
)
// Context is a per-decimal contextual object that governs
// specific operations.
type Context struct {
// MaxScale overrides the MaxScale constant so long as it's
// in the range (0, MaxScale].
MaxScale int
// MinScale overrides the MaxScale constant so long as it's
// in the range [MinScale, 0).
MinScale int
// Precision is the Context's precision; that is, the maximum
// number of significant digits that may result from any
// arithmetic operation. Excluding any package-defined
// constants (e.g., "UnlimitedPrecision"), if precision is
// not in the range [1, MaxPrecision] operations might result
// in an error. A precision of 0 will be interpreted as
// DefaultPrecision. For example,
//
// precision == 4 // 4
// precision == -4 // error
// precision == 0 // DefaultPrecision
// precision == 12 // 12
//
Precision int
// Traps are a set of exceptional conditions that should
// result in an error.
Traps Condition
// Conditions are a set of the most recent exceptional
// conditions to occur during an operation.
Conditions Condition
// RoundingMode determines how a decimal is rounded.
RoundingMode RoundingMode
// OperatingMode which dictates how the decimal operates under certain
// conditions. See OperatingMode for more information.
OperatingMode OperatingMode
}
// dup returns the Context, but with a non-zero Precision.
func (c Context) dup() Context {
ctx := c
ctx.Precision = c.precision()
return ctx
}
func (c Context) precision() int {
if c.Precision != 0 {
return c.Precision
}
return DefaultPrecision
}
func (c Context) emax() int {
if c.MaxScale != 0 {
return c.MaxScale
}
return MaxScale
}
func (c Context) emin() int {
if c.MinScale != 0 {
return c.MinScale
}
return MinScale
}
// Err returns non-nil if there are any trapped exceptional
// conditions.
func (c Context) Err() error {
if m := c.Conditions & c.Traps; m != 0 {
return m
}
return nil
}
// WithContext is shorthand to create a Big decimal from a Context.
func WithContext(c Context) *Big {
z := new(Big)
z.Context = c
return z
}
// WithPrecision is shorthand to create a Big decimal with a given precision.
func WithPrecision(p int) *Big {
z := new(Big)
switch {
case p > 0 && p <= UnlimitedPrecision:
z.Context.Precision = p
case p == 0:
z.Context.Precision = DefaultPrecision
default:
z.setNaN(InvalidContext, qnan, invctxpgtu)
}
return z
}
// The following are called ContextXX instead of DecimalXX
// to reserve the DecimalXX namespace for future decimal types.
// The following Contexts are based on IEEE 754R. Each Context's
// RoundingMode is ToNearestEven, OperatingMode is GDA, and traps
// are set to every exception other than Inexact, Rounded, and
// Subnormal.
var (
// Context32 is the IEEE 754R Decimal32 format.
Context32 = Context{
Precision: 7,
RoundingMode: ToNearestEven,
OperatingMode: GDA,
Traps: ^(Inexact | Rounded | Subnormal),
MaxScale: 96,
MinScale: -95,
}
// Context64 is the IEEE 754R Decimal64 format.
Context64 = Context{
Precision: 16,
RoundingMode: ToNearestEven,
OperatingMode: GDA,
Traps: ^(Inexact | Rounded | Subnormal),
MaxScale: 384,
MinScale: -383,
}
// Context128 is the IEEE 754R Decimal128 format.
Context128 = Context{
Precision: 34,
RoundingMode: ToNearestEven,
OperatingMode: GDA,
Traps: ^(Inexact | Rounded | Subnormal),
MaxScale: 6144,
MinScale: -6143,
}
// ContextUnlimited provides unlimited precision decimals.
ContextUnlimited = Context{
Precision: UnlimitedPrecision,
RoundingMode: ToNearestEven,
OperatingMode: GDA,
Traps: ^(Inexact | Rounded | Subnormal),
MaxScale: MaxScale,
MinScale: MinScale,
}
maxCtx = Context{
Precision: MaxPrecision,
RoundingMode: ToNearestEven,
OperatingMode: GDA,
Traps: ^(Inexact | Rounded | Subnormal),
MaxScale: MaxScale,
MinScale: MinScale,
}
)
// RoundingMode determines how a decimal will be rounded.
type RoundingMode uint8
// The following rounding modes are supported.
const (
ToNearestEven RoundingMode = iota // == IEEE 754-2008 roundTiesToEven
ToNearestAway // == IEEE 754-2008 roundTiesToAway
ToZero // == IEEE 754-2008 roundTowardZero
AwayFromZero // no IEEE 754-2008 equivalent
ToNegativeInf // == IEEE 754-2008 roundTowardNegative
ToPositiveInf // == IEEE 754-2008 roundTowardPositive
ToNearestTowardZero // no IEEE 754-2008 equivalent
unnecessary // placeholder for x / y with UnlimitedPrecision.
)
//go:generate stringer -type RoundingMode
func (m RoundingMode) needsInc(odd bool, r int, pos bool) bool {
switch m {
case AwayFromZero:
return true // always up
case ToZero:
return false // always down
case ToPositiveInf:
return pos // up if positive
case ToNegativeInf:
return !pos // down if negative
// r < 0: closer to higher
// r == 0: halfway
// r > 0: closer to lower
case ToNearestEven:
if r != 0 {
return r > 0
}
return odd
case ToNearestAway:
return r >= 0
case ToNearestTowardZero:
return r > 0
default:
return false
}
}
// OperatingMode dictates how the decimal approaches specific non-numeric
// operations like conversions to strings and panicking on NaNs.
type OperatingMode uint8
const (
// GDA strictly adheres to the General Decimal Arithmetic Specification
// Version 1.70. In particular:
//
// - at does not panic
// - all arithmetic operations will be rounded down to the proper precision
// if necessary
// - it utilizes traps to set both Context.Err and Context.Conditions
// - its string forms of qNaN, sNaN, +Inf, and -Inf are "NaN", "sNaN",
// "Infinity", and "-Infinity", respectively
//
GDA OperatingMode = iota
// Go adheres to typical Go idioms. In particular:
//
// - it panics on NaN values
// - has lossless (i.e., without rounding) addition, subtraction, and
// multiplication
// - traps are ignored; it does not set Context.Err or Context.Conditions
// - its string forms of qNaN, sNaN, +Inf, and -Inf are "NaN", "NaN",
// "+Inf", and "-Inf", respectively
//
Go
)
//go:generate stringer -type OperatingMode
// Condition is a bitmask value raised after or during specific operations. For
// example, dividing by zero is undefined so a DivisionByZero Condition flag
// will be set in the decimal's Context.
type Condition uint32
const (
// Clamped occurs if the scale has been modified to fit the constraints of
// the decimal representation.
Clamped Condition = 1 << iota
// ConversionSyntax occurs when a string is converted to a decimal and does
// not have a valid syntax.
ConversionSyntax
// DivisionByZero occurs when division is attempted with a finite,
// non-zero dividend and a divisor with a value of zero.
DivisionByZero
// DivisionImpossible occurs when the result of integer division would
// contain too many digits (i.e. be longer than the specified precision).
DivisionImpossible
// DivisionUndefined occurs when division is attempted with in which both
// the divided and divisor are zero.
DivisionUndefined
// Inexact occurs when the result of an operation (e.g. division) is not
// exact, or when the Overflow/Underflow Conditions occur.
Inexact
// InsufficientStorage occurs when the system doesn't have enough storage
// (i.e. memory) to store the
InsufficientStorage
// InvalidContext occurs when an invalid context was detected during an
// operation. This might occur if, for example, an invalid RoundingMode was
// passed to a Context.
InvalidContext
// InvalidOperation occurs when:
//
// - an operand to an operation is a signaling NaN
// - an attempt is made to add or subtract infinities of opposite signs
// - an attempt is made to multiply zero by an infinity of either sign
// - an attempt is made to divide an infinity by an infinity
// - the divisor for a remainder operation is zero
// - the dividend for a remainder operation is an infinity
// - either operand of the quantize operation is an infinity, or the result
// of a quantize operation would require greater precision than is
// available
// - the operand of the ln or the log10 operation is less than zero
// - the operand of the square-root operation has a sign of 1 and a
// non-zero coefficient
// - both operands of the power operation are zero, or if the left-hand
// operand is less than zero and the right-hand operand does not have an
// integral value or is an infinity
//
InvalidOperation
// Overflow occurs when the adjusted scale, after rounding, would be
// greater than MaxScale. (Inexact and Rounded will also be raised.)
Overflow
// Rounded occurs when the result of an operation is rounded, or if an
// Overflow/Underflow occurs.
Rounded
// Subnormal ocurs when the result of a conversion or operation is subnormal
// (i.e. the adjusted scale is less than MinScale before any rounding).
Subnormal
// Underflow occurs when the result is inexact and the adjusted scale would
// be smaller (more negative) than MinScale.
Underflow
)
func (c Condition) Error() string { return c.String() }
func (c Condition) String() string {
if c == 0 {
return ""
}
var b strings.Builder
for i := Condition(1); c != 0; i <<= 1 {
if c&i == 0 {
continue
}
switch c ^= i; i {
case Clamped:
b.WriteString("clamped, ")
case ConversionSyntax:
b.WriteString("conversion syntax, ")
case DivisionByZero:
b.WriteString("division by zero, ")
case DivisionImpossible:
b.WriteString("division impossible, ")
case DivisionUndefined:
b.WriteString("division undefined, ")
case Inexact:
b.WriteString("inexact, ")
case InsufficientStorage:
b.WriteString("insufficient storage, ")
case InvalidContext:
b.WriteString("invalid context, ")
case InvalidOperation:
b.WriteString("invalid operation, ")
case Overflow:
b.WriteString("overflow, ")
case Rounded:
b.WriteString("rounded, ")
case Subnormal:
b.WriteString("subnormal, ")
case Underflow:
b.WriteString("underflow, ")
default:
fmt.Fprintf(&b, "unknown(%d), ", i)
}
}
// Omit trailing comma and space.
return b.String()[:b.Len()-2]
}
var _ error = Condition(0)

Просмотреть файл

@ -1,91 +0,0 @@
package decimal
import "testing"
func TestCondition_String(t *testing.T) {
for i, test := range [...]struct {
c Condition
s string
}{
{Clamped, "clamped"},
{Clamped | Underflow, "clamped, underflow"},
{Inexact | Rounded | Subnormal, "inexact, rounded, subnormal"},
{1 << 31, "unknown(2147483648)"},
} {
s := test.c.String()
if s != test.s {
t.Fatalf("#%d: wanted %q, got %q", i, test.s, s)
}
}
}
func TestToNearestAwayQuoRounding(t *testing.T) {
for _, test := range [...]struct {
x string
y string
expected string // x div y round 20
}{
{"1", "9", "0.11111111111111111111"},
{"2", "9", "0.22222222222222222222"},
{"3", "9", "0.33333333333333333333"},
{"4", "9", "0.44444444444444444444"},
{"5", "9", "0.55555555555555555556"},
{"6", "9", "0.66666666666666666667"},
{"7", "9", "0.77777777777777777778"},
{"8", "9", "0.88888888888888888889"},
} {
x, _ := WithContext(Context128).SetString(test.x)
y, _ := WithContext(Context128).SetString(test.y)
z, _ := WithContext(Context128).SetString("0")
z.Context.Precision = 20
z.Context.RoundingMode = ToNearestAway
actual := z.Quo(x, y).String()
expected := test.expected
if actual != expected {
t.Errorf("Quo(%s,%s) result %s, expected %s", test.x, test.y, actual, expected)
}
}
}
func TestNonStandardRoundingModes(t *testing.T) {
for i, test := range [...]struct {
value int64
mode RoundingMode
expected int64
}{
{55, ToNearestTowardZero, 5},
{25, ToNearestTowardZero, 2},
{16, ToNearestTowardZero, 2},
{11, ToNearestTowardZero, 1},
{10, ToNearestTowardZero, 1},
{-10, ToNearestTowardZero, -1},
{-11, ToNearestTowardZero, -1},
{-16, ToNearestTowardZero, -2},
{-25, ToNearestTowardZero, -2},
{-55, ToNearestTowardZero, -5},
{55, AwayFromZero, 6},
{25, AwayFromZero, 3},
{16, AwayFromZero, 2},
{11, AwayFromZero, 2},
{10, AwayFromZero, 1},
{-10, AwayFromZero, -1},
{-11, AwayFromZero, -2},
{-16, AwayFromZero, -2},
{-25, AwayFromZero, -3},
{-55, AwayFromZero, -6},
} {
v := New(test.value, 1)
v.Context.RoundingMode = test.mode
r, ok := v.RoundToInt().Int64()
if !ok {
t.Fatalf("#%d: failed to convert result to int64", i)
}
if test.expected != r {
t.Fatalf("#%d: wanted %d, got %d", i, test.expected, r)
}
}
}

Просмотреть файл

@ -1,273 +0,0 @@
package decimal
import (
"fmt"
)
// Term is a specific term in a continued fraction. A and B correspond with the
// a and b variables of the typical representation of a continued fraction. An
// example can be seen in the book, ``Numerical Recipes in C: The Art of
// Scientific Computing'' (ISBN 0-521-43105-5) in figure 5.2.1 on page 169.
type Term struct {
A, B *Big
}
func (t Term) String() string {
return fmt.Sprintf("[%s / %s]", t.A, t.B)
}
// Generator represents a continued fraction.
type Generator interface {
// Next returns true if there are future terms. Every call to Term—even the
// first—must be preceded by a call to Next. In general, Generators should
// always return true unless an exceptional condition occurs.
Next() bool
// Term returns the next term in the fraction. The caller must not modify
// any of the Term's fields.
Term() Term
}
// Contexter allows Generators to provide a different Context than z's. It's
// intended to be analogous to the relationship between, for example,
// Context.Mul and Big.Mul.
type Contexter interface {
Context() Context
}
// Okay, I _am_ sorry about the name of this interface. It's stupid.
// Walliser is analogous to Lentzer, except it's for the Wallis function.
type Walliser interface {
// Wallis provides the backing storage for a Generator passed to the Wallis
// function. See the Lentzer interface for more information.
Wallis() (a, a1, b, b1, p, eps *Big)
}
type walliser struct {
prec int
}
func (w walliser) Wallis() (a, a1, b, b1, p, eps *Big) {
a = WithPrecision(w.prec)
a1 = WithPrecision(w.prec)
b = WithPrecision(w.prec)
b1 = WithPrecision(w.prec)
p = WithPrecision(w.prec)
eps = New(1, w.prec)
return a, a1, b, b1, p, eps
}
// Wallis sets z to the result of the continued fraction provided
// by the Generator and returns z.
//
// The fraction is evaluated in a top-down manner, using the
// recurrence algorithm discovered by John Wallis. For more
// information on continued fraction representations, see the
// Lentz function.
func (c Context) Wallis(z *Big, g Generator) *Big {
if !g.Next() {
return z
}
ws, ok := g.(Walliser)
if !ok {
ws = walliser{prec: c.precision() + 5}
}
a, a_1, b, b_1, p, eps := ws.Wallis()
t := g.Term()
a_1.SetUint64(1)
a.Copy(t.B)
b.SetUint64(1)
ctx := z.Context
if c, ok := g.(Contexter); ok {
ctx = c.Context()
}
for g.Next() && p.IsFinite() {
t = g.Term()
z.Copy(a)
ctx.FMA(a, a, t.B, ctx.Mul(a_1, a_1, t.A))
a_1.Copy(z)
z.Copy(b)
ctx.FMA(b, b, t.B, ctx.Mul(b_1, b_1, t.A))
b_1.Copy(z)
ctx.Quo(z, a, b)
if ctx.Sub(p, z, p).CmpAbs(eps) <= 0 {
break
}
p.Copy(z)
}
return z
}
// Lentzer, if implemented, allows Generators to provide their own backing
// storage for the Lentz function.
type Lentzer interface {
// Lentz provides the backing storage for a Generator passed to the Lentz
// function.
//
// In Contexter isn't implemented, f, Δ, C, and D should have large enough
// precision to provide a correct result, (See note for the Lentz function.)
//
// eps should be a sufficiently small decimal, likely 1e-15 or smaller.
//
// For more information, refer to "Numerical Recipes in C: The Art of
// Scientific Computing" (ISBN 0-521-43105-5), pg 171.
Lentz() (f, Δ, C, D, eps *Big)
}
// lentzer implements the Lentzer interface.
type lentzer struct{ prec int }
func (l lentzer) Lentz() (f, Δ, C, D, eps *Big) {
f = WithPrecision(l.prec)
Δ = WithPrecision(l.prec)
C = WithPrecision(l.prec)
D = WithPrecision(l.prec)
eps = New(1, l.prec)
return f, Δ, C, D, eps
}
var tiny = New(10, 60)
// Lentz sets z to the result of the continued fraction provided
// by the Generator and returns z.
//
// The continued fraction should be represented as such:
//
// a1
// f(x) = b0 + --------------------
// a2
// b1 + ---------------
// a3
// b2 + ----------
// a4
// b3 + -----
// ...
//
// Or, equivalently:
//
// a1 a2 a3
// f(x) = b0 + ---- ---- ----
// b1 + b2 + b3 + ···
//
// If terms need to be subtracted, the a_N terms should be
// negative. To compute a continued fraction without b_0, divide
// the result by a_1.
//
// If the first call to the Generator's Next method returns
// false, the result of Lentz is undefined.
//
// Note: the accuracy of the result may be affected by the
// precision of intermediate results. If larger precision is
// desired, it may be necessary for the Generator to implement
// the Lentzer interface and set a higher precision for f, Δ, C,
// and D.
func (c Context) Lentz(z *Big, g Generator) *Big {
// We use the modified Lentz algorithm from
// "Numerical Recipes in C: The Art of Scientific Computing" (ISBN
// 0-521-43105-5), pg 171.
//
// Set f0 = b0; if b0 = 0 set f0 = tiny.
// Set C0 = f0.
// Set D0 = 0.
// For j = 1, 2,...
// Set D_j = b_j+a_j*D{_j1}.
// If D_j = 0, set D_j = tiny.
// Set C_j = b_j+a_j/C{_j1}.
// If C_j = 0, set C_j = tiny.
// Set D_j = 1/D_j.
// Set ∆_j = C_j*D_j.
// Set f_j = f{_j-1}∆j.
// If |∆_j - 1| < eps then exit.
if !g.Next() {
return z
}
// See if our Generator provides us with backing storage.
lz, ok := g.(Lentzer)
if !ok {
// TODO(eric): what is a sensible default precision?
lz = lentzer{prec: c.precision() + 5}
}
f, Δ, C, D, eps := lz.Lentz()
// tiny should be less than typical values of eps.
tiny := tiny
if eps.Scale() > tiny.Scale() {
tiny = New(10, min(eps.Scale()*2, c.emax()))
}
t := g.Term()
if t.B.Sign() != 0 {
f.Copy(t.B)
} else {
f.Copy(tiny)
}
C.Copy(f)
D.SetUint64(0)
ctx := z.Context
if c, ok := g.(Contexter); ok {
ctx = c.Context()
}
for g.Next() && f.IsFinite() {
t = g.Term()
// Set D_j = b_j + a_j*D{_j-1}
// Reuse D for the multiplication.
ctx.FMA(D, t.A, D, t.B) // D.Add(t.B, D.Mul(t.A, D))
// If D_j = 0, set D_j = tiny
if D.Sign() == 0 {
D.Copy(tiny)
}
// Set C_j = b_j + a_j/C{_j-1}
// Reuse C for the division.
ctx.Add(C, t.B, ctx.Quo(C, t.A, C))
// If C_j = 0, set C_j = tiny
if C.Sign() == 0 {
C.Copy(tiny)
}
// Set D_j = 1/D_j
ctx.Quo(D, one.get(), D)
// Set Δ_j = C_j*D_j
ctx.Mul(Δ, C, D)
// Set f_j = f{_j-1}*Δ_j
ctx.Mul(f, f, Δ)
// If |Δ_j - 1| < eps then exit
if ctx.Sub(Δ, Δ, one.get()).CmpAbs(eps) < 0 {
break
}
}
z.Context.Conditions |= f.Context.Conditions
return ctx.Set(z, f)
}
/*
func dump(f, Δ, D, C, eps *Big) {
fmt.Printf(`
f : %s
Δ : %s
D : %s
C : %s
eps: %s
`, f, Δ, D, C, eps)
}
*/

Просмотреть файл

@ -1,96 +0,0 @@
package decimal
import (
"encoding/binary"
"fmt"
"math"
"math/big"
)
// decomposer composes or decomposes a decimal value to and from individual parts.
// There are four separate parts: a boolean negative flag, a form byte with three possible states
// (finite=0, infinite=1, NaN=2), a base-2 big-endian integer
// coefficient (also known as a significand) as a []byte, and an int32 exponent.
// These are composed into a final value as "decimal = (neg) (form=finite) coefficient * 10 ^ exponent".
// A zero length coefficient is a zero value.
// If the form is not finite the coefficient and scale should be ignored.
// The negative parameter may be set to true for any form, although implementations are not required
// to respect the negative parameter in the non-finite form.
//
// Implementations may choose to signal a negative zero or negative NaN, but implementations
// that do not support these may also ignore the negative zero or negative NaN without error.
// If an implementation does not support Infinity it may be converted into a NaN without error.
// If a value is set that is larger then what is supported by an implementation is attempted to
// be set, an error must be returned.
// Implementations must return an error if a NaN or Infinity is attempted to be set while neither
// are supported.
type decomposer interface {
// Decompose returns the internal decimal state into parts.
// If the provided buf has sufficient capacity, buf may be returned as the coefficient with
// the value set and length set as appropriate.
Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32)
// Compose sets the internal decimal value from parts. If the value cannot be
// represented then an error should be returned.
// The coefficent should not be modified. Successive calls to compose with
// the same arguments should result in the same decimal value.
Compose(form byte, negative bool, coefficient []byte, exponent int32) error
}
// Decompose returns the internal decimal state into parts.
// If the provided buf has sufficient capacity, buf may be returned as the coefficient with
// the value set and length set as appropriate.
func (z *Big) Decompose(buf []byte) (form byte, negative bool, coefficient []byte, exponent int32) {
negative = z.Sign() < 0
switch {
case z.IsInf(0):
form = 1
return
case z.IsNaN(0):
form = 2
return
}
if !z.IsFinite() {
panic("expected number to be finite")
}
if z.exp > math.MaxInt32 {
panic("exponent exceeds max size")
}
exponent = int32(z.exp)
if z.isCompact() {
if cap(buf) >= 8 {
coefficient = buf[:8]
} else {
coefficient = make([]byte, 8)
}
binary.BigEndian.PutUint64(coefficient, z.compact)
} else {
coefficient = z.unscaled.Bytes() // This returns a big-endian slice.
}
return
}
// Compose sets the internal decimal value from parts. If the value cannot be
// represented then an error should be returned.
func (z *Big) Compose(form byte, negative bool, coefficient []byte, exponent int32) error {
switch form {
default:
return fmt.Errorf("unknown form: %v", form)
case 0:
// Finite form below.
case 1:
z.SetInf(negative)
return nil
case 2:
z.SetNaN(false)
return nil
}
bigc := &big.Int{}
bigc.SetBytes(coefficient)
z.SetBigMantScale(bigc, -int(exponent))
if negative {
z.Neg(z)
}
return nil
}

Просмотреть файл

@ -1,99 +0,0 @@
package decimal
import (
"fmt"
"testing"
)
func TestDecomposerRoundTrip(t *testing.T) {
list := []struct {
N string // Name.
S string // String value.
E bool // Expect an error.
}{
{N: "Normal-1", S: "123.456"},
{N: "Normal-2", S: "-123.456"},
{N: "NaN-1", S: "NaN"},
{N: "NaN-2", S: "-NaN"},
{N: "Infinity-1", S: "Infinity"},
{N: "Infinity-2", S: "-Infinity"},
}
for _, item := range list {
t.Run(item.N, func(t *testing.T) {
d := &Big{}
d, ok := d.SetString(item.S)
if !ok {
t.Fatal("unable to set value")
}
set, set2 := &Big{}, &Big{}
f, n, c, e := d.Decompose(nil)
err := set.Compose(f, n, c, e)
if err == nil && item.E {
t.Fatal("expected error, got <nil>")
}
err = set2.Compose(f, n, c, e)
if err == nil && item.E {
t.Fatal("expected error, got <nil>")
}
if set.Cmp(set2) != 0 {
t.Fatalf("composing the same value twice resulted in different values. set=%v set2=%v", set, set2)
}
if err != nil && !item.E {
t.Fatalf("unexpected error: %v", err)
}
if set.Cmp(d) != 0 {
t.Fatalf("values incorrect, got %v want %v (%s)", set, d, item.S)
}
})
}
}
func TestDecomposerCompose(t *testing.T) {
t.Skipf("not working since MySQL changes")
list := []struct {
N string // Name.
S string // String value.
Form byte // Form
Neg bool
Coef []byte // Coefficent
Exp int32
Err bool // Expect an error.
}{
{N: "Zero", S: "0", Coef: nil, Exp: 0},
{N: "Normal-1", S: "123.456", Coef: []byte{0x01, 0xE2, 0x40}, Exp: -3},
{N: "Neg-1", S: "-123.456", Neg: true, Coef: []byte{0x01, 0xE2, 0x40}, Exp: -3},
{N: "PosExp-1", S: "123456000", Coef: []byte{0x01, 0xE2, 0x40}, Exp: 3},
{N: "PosExp-2", S: "-123456000", Neg: true, Coef: []byte{0x01, 0xE2, 0x40}, Exp: 3},
{N: "AllDec-1", S: "0.123456", Coef: []byte{0x01, 0xE2, 0x40}, Exp: -6},
{N: "AllDec-2", S: "-0.123456", Neg: true, Coef: []byte{0x01, 0xE2, 0x40}, Exp: -6},
{N: "NaN-1", S: "NaN", Form: 2},
{N: "Infinity-1", S: "Infinity", Form: 1},
{N: "Infinity-2", S: "-Infinity", Form: 1, Neg: true},
}
for _, item := range list {
t.Run(item.N, func(t *testing.T) {
d := &Big{}
d, ok := d.SetString(item.S)
if !ok {
t.Fatal("unable to set value")
}
err := d.Compose(item.Form, item.Neg, item.Coef, item.Exp)
if err != nil && !item.Err {
t.Fatalf("unexpected error, got %v", err)
}
if item.Err {
if err == nil {
t.Fatal("expected error, got <nil>")
}
return
}
if s := fmt.Sprintf("%f", d); s != item.S {
t.Fatalf("unexpected value, got %q want %q", s, item.S)
}
})
}
}

Просмотреть файл

@ -1,108 +0,0 @@
// Package decimal provides a high-performance, arbitrary precision,
// floating-point decimal library.
//
// Overview
//
// This package provides floating-point decimal numbers, useful for financial
// programming or calculations where a larger, more accurate representation of
// a number is required.
//
// In addition to basic arithmetic operations (addition, subtraction,
// multiplication, and division) this package offers various mathematical
// functions, including the exponential function, various logarithms, and the
// ability to compute continued fractions.
//
// While lean, this package is full of features. It implements interfaces like
// ``fmt.Formatter'' and intuitively utilizes verbs and flags as described in
// the ``fmt'' package. (Also included: ``fmt.Scanner'', ``fmt.Stringer'',
// ``encoding.TextUnmarshaler'', and ``encoding.TextMarshaler''.)
//
// It allows users to specific explicit contexts for arithmetic operations, but
// doesn't require it. It provides access to NaN payloads and is more lenient
// when parsing a decimal from a string than the GDA specification requires.
//
// API interfaces have been changed slightly to work more seamlessly with
// existing Go programs. For example, many ``Quantize'' implementations require
// a decimal as both the receiver and argument which isn't very user friendly.
// Instead, this library accepts a simple ``int'' which can be derived from an
// existing decimal if required.
//
// It contains two modes of operation designed to make transitioning to various
// GDA "quirks" (like always rounding lossless operations) easier.
//
// GDA: strictly adhere to the GDA specification (default)
// Go: utilize Go idioms, more flexibility
//
// Goals
//
// There are three primary goals of this library:
//
// 1. Correctness
//
// By adhering to the General Decimal Arithmetic specification, this package
// has a well-defined structure for its arithmetic operations.
//
// 2. Performance
//
// Decimal libraries are inherently slow; this library works diligently to
// minimize memory allocations and utilize efficient algorithms. Performance
// regularly benchmarks as fast or faster than many other popular decimal
// libraries.
//
// 3. Ease of use
//
// Libraries should be intuitive and work out of the box without having to
// configure too many settings; however, precise settings should still be
// available.
//
// Usage
//
// The following type is supported:
//
// Big decimal numbers
//
// The zero value for a Big corresponds with 0, meaning all the following are
// valid:
//
// var x Big
// y := new(Big)
// z := &Big{}
//
// Method naming is the same as math/big's, meaning:
//
// func (z *T) SetV(v V) *T // z = v
// func (z *T) Unary(x *T) *T // z = unary x
// func (z *T) Binary(x, y *T) *T // z = x binary y
// func (x *T) Pred() P // p = pred(x)
//
// In general, its conventions mirror math/big's. It is suggested to read the
// math/big package comments to gain an understanding of this package's
// conventions.
//
// Arguments to Binary and Unary methods are allowed to alias, so the following
// is valid:
//
// x := New(1, 0)
// x.Add(x, x) // x == 2
//
// y := New(1, 0)
// y.FMA(y, x, y) // y == 3
//
// Unless otherwise specified, the only argument that will be modified is the
// result (``z''). This means the following is valid and race-free:
//
// x := New(1, 0)
// var g1, g2 Big
//
// go func() { g1.Add(x, x) }()
// go func() { g2.Add(x, x) }()
//
// But this is not:
//
// x := New(1, 0)
// var g Big
//
// go func() { g.Add(x, x) }() // BAD! RACE CONDITION!
// go func() { g.Add(x, x) }() // BAD! RACE CONDITION!
//
package decimal

Просмотреть файл

@ -1,348 +0,0 @@
//nolint:errcheck
package decimal
import (
"bytes"
"fmt"
"io"
"math/big"
"strconv"
)
// allZeros returns true if every character in b is '0'.
func allZeros(b []byte) bool {
for _, c := range b {
if c != '0' {
return false
}
}
return true
}
var zero = []byte{'0'}
// roundString rounds the plain numeric string (e.g., "1234") b.
func roundString(b []byte, mode RoundingMode, pos bool, prec int) []byte {
if prec >= len(b) {
return append(b, bytes.Repeat(zero, prec-len(b))...)
}
// Trim zeros until prec. This is useful when we can round exactly by simply
// chopping zeros off the end of the number.
if allZeros(b[prec:]) {
return b[:prec]
}
b = b[:prec+1]
i := prec - 1
// Blindly increment b[i] and handle possible carries later.
switch mode {
case AwayFromZero:
b[i]++
case ToZero:
// OK
case ToPositiveInf:
if pos {
b[i]++
}
case ToNegativeInf:
if !pos {
b[i]++
}
case ToNearestEven:
if b[i+1] > '5' || b[i+1] == '5' && b[i]%2 != 0 {
b[i]++
}
case ToNearestAway:
if b[i+1] >= '5' {
b[i]++
}
case ToNearestTowardZero:
if b[i+1] > '5' {
b[i]++
}
}
if b[i] != '9'+1 {
return b[:prec]
}
// We had to carry.
b[i] = '0'
for i--; i >= 0; i-- {
if b[i] != '9' {
b[i]++
break
}
b[i] = '0'
}
// Carried all the way over to the first column, so slide the buffer down
// and instead of reallocating.
if b[0] == '0' {
copy(b[1:], b)
b[0] = '1'
// We might end up with an extra digit of precision. E.g., given the
// decimal 9.9 with a requested precision of 1, we'd convert 99 -> 10.
// Let the calling code handle that case.
prec++
}
return b[:prec]
}
// formatCompact formats the compact decimal, x, as an unsigned integer.
func formatCompact(x uint64) []byte {
var b [20]byte
return strconv.AppendUint(b[0:0], uint64(x), 10)
}
// formatUnscaled formats the unscaled (non-compact) decimal, unscaled, as an
// unsigned integer.
func formatUnscaled(unscaled *big.Int) []byte {
// math/big.MarshalText never returns an error, only nil, so there's no need
// to check for an error. Use MarshalText instead of Append because it limits
// us to one allocation.
b, _ := unscaled.MarshalText()
if b[0] == '-' {
b = b[1:]
}
return b
}
// noWidth indicates the width of a formatted number wasn't set.
const noWidth = -1
type format byte
const (
normal format = iota // either sci or plain, depending on x
plain // forced plain
sci // forced sci
)
//go:generate stringer -type=format
type formatter struct {
w interface {
io.Writer
io.ByteWriter
WriteString(string) (int, error)
}
sign byte // leading '+' or ' ' flag
prec int // total precision
width int // min width
n int64 // cumulative number of bytes written to w
}
func (f *formatter) WriteByte(c byte) error {
f.n++
return f.w.WriteByte(c)
}
func (f *formatter) WriteString(s string) (int, error) {
m, err := f.w.WriteString(s)
f.n += int64(m)
return m, err
}
func (f *formatter) Write(p []byte) (n int, err error) {
n, err = f.w.Write(p)
f.n += int64(n)
return n, err
}
var sciE = [2]byte{GDA: 'E', Go: 'e'}
func (f *formatter) format(x *Big, format format, e byte, rounding RoundingMode) {
if x == nil {
f.WriteString("<nil>")
return
}
o := x.Context.OperatingMode
if x.isSpecial() {
switch o {
case GDA:
f.WriteString(x.form.String())
if x.IsNaN(0) && x.compact != 0 {
f.WriteString(strconv.FormatUint(x.compact, 10))
}
case Go:
if x.IsNaN(0) {
f.WriteString("NaN")
} else if x.IsInf(+1) {
f.WriteString("+Inf")
} else {
f.WriteString("-Inf")
}
}
return
}
if x.isZero() && o == Go {
// Go mode prints zeros different than GDA.
if f.width == noWidth {
f.WriteByte('0')
} else {
f.WriteString("0.")
io.CopyN(f, zeroReader{}, int64(f.width))
}
return
}
neg := x.Signbit()
if neg {
f.WriteByte('-')
} else if f.sign != 0 {
f.WriteByte(f.sign)
}
var (
b []byte
exp int
)
if f.prec > 0 {
if x.isCompact() {
b = formatCompact(x.compact)
} else {
b = formatUnscaled(&x.unscaled)
}
orig := len(b)
b = roundString(b, rounding, !neg, f.prec)
exp = int(x.exp) + orig - len(b)
} else if f.prec < 0 {
f.prec = -f.prec
exp = -f.prec
} else {
b = []byte{'0'}
}
// "Next, the adjusted exponent is calculated; this is the exponent, plus
// the number of characters in the converted coefficient, less one. That
// is, exponent+(clength-1), where clength is the length of the coefficient
// in decimal digits.
adj := exp + (len(b) - 1)
if format != sci {
if exp <= 0 && (format == plain || adj >= -6) {
// "If the exponent is less than or equal to zero and the adjusted
// exponent is greater than or equal to -6 the number will be
// converted to a character form without using exponential notation."
//
// - http://speleotrove.com/decimal/daconvs.html#reftostr
f.formatPlain(b, exp)
return
}
// No decimal places, write b and fill with zeros.
if format == plain && exp > 0 {
f.Write(b)
io.CopyN(f, zeroReader{}, int64(exp))
return
}
}
f.formatSci(b, adj, e)
}
// formatSci returns the scientific version of b.
func (f *formatter) formatSci(b []byte, adj int, e byte) {
f.WriteByte(b[0])
if len(b) > 1 {
f.WriteByte('.')
f.Write(b[1:])
}
// If negative, the call to strconv.Itoa will add the minus sign for us.
f.WriteByte(e)
if adj > 0 {
f.WriteByte('+')
}
f.WriteString(strconv.Itoa(adj))
}
// formatPlain returns the plain string version of b.
func (f *formatter) formatPlain(b []byte, exp int) {
const zeroRadix = "0."
switch radix := len(b) + exp; {
// log10(b) == scale, so immediately before b: 0.123456
case radix == 0:
f.WriteString(zeroRadix)
f.Write(b)
// log10(b) > scale, so somewhere inside b: 123.456
case radix > 0:
f.Write(b[:radix])
if radix < len(b) {
f.WriteByte('.')
f.Write(b[radix:])
}
// log10(b) < scale, so before p "0s" and before b: 0.00000123456
default:
f.WriteString(zeroRadix)
io.CopyN(f, zeroReader{}, -int64(radix))
end := len(b)
if f.prec < end {
end = f.prec
}
f.Write(b[:end])
}
}
// TODO(eric): can we merge zeroReader and spaceReader into a "singleReader" or
// something and still maintain the same performance?
// zeroReader is an io.Reader that, when read from, only provides the character
// '0'.
type zeroReader struct{}
// Read implements io.Reader.
func (z zeroReader) Read(p []byte) (n int, err error) {
// zeroLiterals is 16 '0' bytes. It's used to speed up zeroReader's Read
// method.
const zeroLiterals = "0000000000000000"
for n < len(p) {
m := copy(p[n:], zeroLiterals)
if m == 0 {
break
}
n += m
}
return n, nil
}
// spaceReader is an io.Reader that, when read from, only provides the
// character ' '.
type spaceReader struct{}
// Read implements io.Reader.
func (s spaceReader) Read(p []byte) (n int, err error) {
// spaceLiterals is 16 ' ' bytes. It's used to speed up spaceReader's Read
// method.
const spaceLiterals = " "
for n < len(p) {
m := copy(p[n:], spaceLiterals)
if m == 0 {
break
}
n += m
}
return n, nil
}
// stateWrapper is a wrapper around an io.Writer to add WriteByte and
// WriteString methods.
type stateWrapper struct{ fmt.State }
func (w stateWrapper) WriteByte(c byte) error {
_, err := w.Write([]byte{c})
return err
}
func (w stateWrapper) WriteString(s string) (int, error) {
return io.WriteString(w.State, s)
}

Просмотреть файл

@ -1,25 +0,0 @@
// Code generated by "stringer -type=format"; DO NOT EDIT.
package decimal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[normal-0]
_ = x[plain-1]
_ = x[sci-2]
}
const _format_name = "normalplainsci"
var _format_index = [...]uint8{0, 6, 11, 14}
func (i format) String() string {
if i >= format(len(_format_index)-1) {
return "format(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _format_name[_format_index[i]:_format_index[i+1]]
}

Просмотреть файл

@ -1,89 +0,0 @@
package decimal
import (
"fmt"
"testing"
)
func TestRoundString(t *testing.T) {
type roundStringTest struct {
input string
mode RoundingMode
prec int
expect string
}
// From https://en.wikipedia.org/wiki/IEEE_floating_point#Rounding_rules
// Formatted with https://ozh.github.io/ascii-tables/
//
// +---------------------------------+-------+-------+-------+-------+
// | Mode / Example Value | +11.5 | +12.5 | 11.5 | 12.5 |
// +---------------------------------+-------+-------+-------+-------+
// | to nearest, ties to even | +12.0 | +12.0 | 12.0 | 12.0 |
// | to nearest, ties away from zero | +12.0 | +13.0 | 12.0 | 13.0 |
// | toward 0 | +11.0 | +12.0 | 11.0 | 12.0 |
// | toward +∞ | +12.0 | +13.0 | 11.0 | 12.0 |
// | toward −∞ | +11.0 | +12.0 | 12.0 | 13.0 |
// +---------------------------------+-------+-------+-------+-------+
makeWikiTests := func(mode RoundingMode, out ...string) []roundStringTest {
var tests [4]roundStringTest
for i, inp := range [...]string{"+115", "+125", "-115", "-125"} {
tests[i] = roundStringTest{inp, mode, 2, out[i]}
}
return tests[:]
}
even := makeWikiTests(ToNearestEven, "12", "12", "12", "12")
away := makeWikiTests(ToNearestAway, "12", "13", "12", "13")
zero := makeWikiTests(ToZero, "11", "12", "11", "12")
pinf := makeWikiTests(ToPositiveInf, "12", "13", "11", "12")
ninf := makeWikiTests(ToNegativeInf, "11", "12", "12", "13")
tests := []roundStringTest{
{"+12345", ToNearestEven, 4, "1234"},
{"+12349", ToNearestEven, 4, "1235"},
{"+12395", ToNearestEven, 4, "1240"},
{"+99", ToNearestEven, 1, "10"},
{"+400", ToZero /* mode is irrelevant */, 1, "4"},
}
tests = append(tests, even...)
tests = append(tests, away...)
tests = append(tests, zero...)
tests = append(tests, pinf...)
tests = append(tests, ninf...)
for i, test := range tests {
pos := test.input[0] == '+'
inp := test.input[1:]
got := roundString([]byte(inp), test.mode, pos, test.prec)
if string(got) != test.expect {
t.Fatalf(`#%d:
[round(%q, %s, %d)]
got : %q
wanted: %q
`, i, test.input, test.mode, test.prec, got, test.expect)
}
}
}
func TestDecimal_Format(t *testing.T) {
for i, s := range [...]struct {
format string
input string
want string
}{
{"%.10f", "0.1234567891", "0.1234567891"},
{"%.10f", "0.01", "0.0100000000"},
{"%.10f", "0.0000000000000000000000000000000000000000000000000000000000001", "0.0000000000"},
} {
z, _ := new(Big).SetString(s.input)
got := fmt.Sprintf(s.format, z)
if got != s.want {
t.Fatalf(`#%d: printf(%s)
got : %s
wanted: %s
`, i, s.format, got, s.want)
}
}
}

Просмотреть файл

@ -1,66 +0,0 @@
package arith
import (
"math/big"
"math/bits"
)
// Abs returns the absolute value of x.
func Abs(x int64) uint64 {
m := x >> 63
return uint64((x ^ m) - m)
}
// Cmp compares x and y and returns
//
// -1 if x < 0
// 0 if x == 0
// +1 if x > 0
//
func Cmp(x, y uint64) int {
if x != y {
if x > y {
return +1
}
return -1
}
return 0
}
// CmpShift compares x and y*shift.
func CmpShift(x, y, shift uint64) int {
y1, y0 := bits.Mul64(y, shift)
if y1 != 0 {
return -1
}
return Cmp(x, y0)
}
// CmpBits compares x and y.
func CmpBits(x, y []big.Word) (r int) {
// Copied from math/big.nat.go
m := len(x)
n := len(y)
if m != n || m == 0 {
switch {
case m < n:
r = -1
case m > n:
r = 1
}
return
}
i := m - 1
for i > 0 && x[i] == y[i] {
i--
}
switch {
case x[i] < y[i]:
r = -1
case x[i] > y[i]:
r = 1
}
return
}

Просмотреть файл

@ -1,232 +0,0 @@
// Package arith provides performance-sensitive arithmetic
// operations.
package arith
import (
"math/big"
"math/bits"
)
const (
intSize = 32 << (^uint(0) >> 63) // 32 or 64
MaxInt = 1<<(intSize-1) - 1
)
// Words returns a little-endian slice of big.Words representing
// the uint64.
func Words(x uint64) []big.Word {
if bits.UintSize == 32 {
return []big.Word{big.Word(x), big.Word(x >> 32)}
}
return []big.Word{big.Word(x)}
}
// Add sets z to x + y and returns z.
//
// x is assumed to be unsigned.
func Add(z, x *big.Int, y uint64) *big.Int {
zw := z.Bits()
switch xw := x.Bits(); {
default:
zw = add(zw, xw, big.Word(y))
case len(xw) == 0:
zw = setW(zw, big.Word(y))
case y == 0:
zw = set(zw, xw)
}
return z.SetBits(zw)
}
// Sub sets z to x - y and returns z.
//
// x is assumed to be unsigned.
func Sub(z, x *big.Int, y uint64) *big.Int {
zw := z.Bits()
switch xw := x.Bits(); {
default:
zw = sub(zw, xw, big.Word(y))
case y == 0:
zw = set(zw, xw)
case len(xw) == 0:
panic("underflow")
}
return z.SetBits(zw)
}
// Mul sets z to x * y and returns z.
//
// x is assumed to be unsigned.
func Mul(z, x *big.Int, y uint64) *big.Int {
if y == 0 || x.Sign() == 0 {
return z.SetUint64(0)
}
return z.SetBits(mulAddWW(z.Bits(), x.Bits(), big.Word(y), 0))
}
// MulPow10 computes x * 10**n and a bool indicating whether the
// multiplcation was successful.
func MulPow10(x uint64, n uint64) (uint64, bool) {
p, ok := Pow10(n)
if !ok {
// 0 * 10^n = 0.
return 0, x == 0
}
hi, lo := bits.Mul64(x, p)
return lo, hi == 0
}
// MulBigPow10 sets z to x * 10**n and returns z.
func MulBigPow10(z, x *big.Int, n uint64) *big.Int {
switch {
case x.Sign() == 0:
return z.SetUint64(0)
case n == 0:
return z.Set(x)
default:
return z.Mul(x, BigPow10(n))
}
}
// Set sets z to the 128-bit integer represented by z1 and z0.
func Set(z *big.Int, z1, z0 uint64) *big.Int {
ww := makeWord(z.Bits(), 128/bits.UintSize)
if bits.UintSize == 32 {
ww[3] = big.Word(z1 >> 32)
ww[2] = big.Word(z1)
ww[1] = big.Word(z0 >> 32)
ww[0] = big.Word(z0)
} else {
ww[1] = big.Word(z1)
ww[0] = big.Word(z0)
}
return z.SetBits(ww)
}
// The following is (mostly) copied from math/big/arith.go, licensed under the
// BSD 3-clause license: https://github.com/golang/go/blob/master/LICENSE
func makeWord(z []big.Word, n int) []big.Word {
if n <= cap(z) {
return z[:n]
}
const e = 4
return make([]big.Word, n, n+e)
}
func norm(z []big.Word) []big.Word {
i := len(z)
for i > 0 && z[i-1] == 0 {
i--
}
return z[0:i]
}
func mulWW(x, y big.Word) (z1, z0 big.Word) {
zz1, zz0 := bits.Mul(uint(x), uint(y))
return big.Word(zz1), big.Word(zz0)
}
func addWW(x, y, c big.Word) (z1, z0 big.Word) {
zz1, zz0 := bits.Add(uint(x), uint(y), uint(c))
return big.Word(zz0), big.Word(zz1)
}
func subWW(x, y, c big.Word) (z1, z0 big.Word) {
zz1, zz0 := bits.Sub(uint(x), uint(y), uint(c))
return big.Word(zz0), big.Word(zz1)
}
func mulAddWW(z, x []big.Word, y, r big.Word) []big.Word {
m := len(x)
z = makeWord(z, m+1)
z[m] = mulAddVWW(z[0:m], x, y, r)
return norm(z)
}
func mulAddVWW(z, x []big.Word, y, r big.Word) (c big.Word) {
c = r
for i := range z {
c, z[i] = mulAddWWW(x[i], y, c)
}
return c
}
func mulAddWWW(x, y, c big.Word) (z1, z0 big.Word) {
z1, zz0 := mulWW(x, y)
if z0 = zz0 + c; z0 < zz0 {
z1++
}
return z1, z0
}
func set(z, x []big.Word) []big.Word {
z = makeWord(z, len(x))
copy(z, x)
return z
}
func setW(z []big.Word, x big.Word) []big.Word {
z = makeWord(z, 1)
z[0] = x
return z
}
// add sets z to x + y and returns z.
func add(z, x []big.Word, y big.Word) []big.Word {
m := len(x)
const n = 1
// m > 0 && y > 0
z = makeWord(z, m+1)
var c big.Word
// addVV(z[0:m], x, y) but WW since len(y) == 1
c, z[0] = addWW(x[0], y, 0)
if m > n {
c = addVW(z[n:m], x[n:], c)
}
z[m] = c
return norm(z)
}
// sub sets z to x - y and returns z.
func sub(z, x []big.Word, y big.Word) []big.Word {
m := len(x)
const n = 1
// m > 0 && y > 0
z = makeWord(z, m)
// subVV(z[0:m], x, y) but WW since len(y) == 1
var c big.Word
c, z[0] = subWW(x[0], y, 0)
if m > n {
c = subVW(z[n:], x[n:], c)
}
if c != 0 {
panic("underflow")
}
return norm(z)
}
// addVW sets z to x + y and returns the carry.
func addVW(z, x []big.Word, y big.Word) (c big.Word) {
c = y
for i, xi := range x[:len(z)] {
zi := xi + c
z[i] = zi
c = xi &^ zi >> (bits.UintSize - 1)
}
return c
}
// subVW sets z to x - y and returns the carry.
func subVW(z, x []big.Word, y big.Word) (c big.Word) {
c = y
for i, xi := range x[:len(z)] {
zi := xi - c
z[i] = zi
c = zi &^ xi >> (bits.UintSize - 1)
}
return c
}

Просмотреть файл

@ -1,67 +0,0 @@
package arith
import (
"math"
"math/big"
"strings"
"testing"
)
func B(s string) *big.Int {
x, _ := new(big.Int).SetString(s, 10)
return x
}
var testCases = [...]struct {
x *big.Int
y uint64
}{
{B("18446744073709551616"), 1},
{B("18446744073709551617"), 3},
{B("18446744073709551617"), math.MaxUint64},
{B("36893488147419103230"), math.MaxUint64},
{B("36893488147419103230"), 42},
{B("221360928884514619380432421349238409238490238423423"), math.MaxUint64},
{B(strings.Repeat("2", 500)), math.MaxUint64},
}
func testFn(t *testing.T,
fn1 func(z, x, y *big.Int) *big.Int,
fn2 func(z, x *big.Int, y uint64) *big.Int,
) {
for _, tc := range testCases {
x := new(big.Int).Set(tc.x)
y0 := new(big.Int).SetUint64(tc.y)
r := fn1(y0, x, y0)
r0 := fn2(new(big.Int), x, tc.y)
if r.Cmp(r0) != 0 {
t.Fatalf(`
r : %s
r0: %s`, r, r0)
}
r1 := fn2(x, x, tc.y)
if r.Cmp(r1) != 0 {
t.Fatalf(`
r : %s
r1: %s`, r, r1)
}
}
}
func TestAdd(t *testing.T) { testFn(t, (*big.Int).Add, Add) }
func TestSub(t *testing.T) { testFn(t, (*big.Int).Sub, Sub) }
func TestMul(t *testing.T) { testFn(t, (*big.Int).Mul, Mul) }
func benchmarkFn(b *testing.B, fn func(z, x *big.Int, y uint64) *big.Int) {
var z big.Int
for i := 0; i < b.N; i++ {
tc := testCases[i%len(testCases)]
fn(&z, tc.x, tc.y)
}
}
func BenchmarkAdd(b *testing.B) { benchmarkFn(b, Add) }
func BenchmarkSub(b *testing.B) { benchmarkFn(b, Sub) }
func BenchmarkMul(b *testing.B) { benchmarkFn(b, Mul) }

Просмотреть файл

@ -1,54 +0,0 @@
package arith
import (
"math/big"
"math/bits"
)
const MaxLength = 20
// Length returns the number of digits in x.
func Length(x uint64) int {
if x < 10 {
return 1
}
// From https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
r := int((bits.Len64(x) * 1233) >> 12)
if p, _ := Pow10(uint64(r)); x < p {
return r
}
return r + 1
}
// BigLength returns the number of digits in x.
func BigLength(x *big.Int) int {
if x.Sign() == 0 {
return 1
}
var (
m uint64
nb = uint64(x.BitLen())
)
// overflowCutoff is the largest number where N * 0x268826A1 <= 1<<63 - 1
const overflowCutoff = 14267572532
if nb > overflowCutoff {
// Given the identity ``log_n a + log_n b = log_n a*b''
// and ``(1<<63 - 1) / overflowCutoff < overFlowCutoff''
// we can break nb into two factors: overflowCutoff and X.
// overflowCutoff / log10(2)
m = 1<<32 - 1
nb = (nb / overflowCutoff) + (nb % overflowCutoff)
}
// 0x268826A1/2^31 is an approximation of log10(2). See ilog10.
// The more accurate approximation 0x268826A13EF3FE08/2^63 overflows.
m += ((nb + 1) * 0x268826A1) >> 31
if x.CmpAbs(BigPow10(m)) < 0 {
return int(m)
}
return int(m + 1)
}

Просмотреть файл

@ -1,138 +0,0 @@
package arith
import (
"crypto/rand"
"io"
"math/big"
mrand "math/rand"
"testing"
)
func TestLength(t *testing.T) {
tests := [...]struct {
i uint64
l int
}{
{i: 0, l: 1},
{i: 1, l: 1},
{i: 10, l: 2},
{i: 100, l: 3},
{i: 1000, l: 4},
{i: 10000, l: 5},
{i: 100000, l: 6},
{i: 1000000, l: 7},
{i: 10000000, l: 8},
{i: 100000000, l: 9},
{i: 1000000000, l: 10},
{i: 10000000000, l: 11},
{i: 100000000000, l: 12},
{i: 1000000000000, l: 13},
{i: 10000000000000, l: 14},
{i: 100000000000000, l: 15},
{i: 1000000000000000, l: 16},
{i: 10000000000000000, l: 17},
{i: 100000000000000000, l: 18},
{i: 1000000000000000000, l: 19},
{i: 10000000000000000000, l: 20},
}
for _, v := range tests {
if l := Length(v.i); l != v.l {
t.Fatalf("#%d: wanted %d, got %d", v.i, v.l, l)
}
}
}
func TestBigLength(t *testing.T) {
tests := [...]struct {
i *big.Int
l int
}{
{i: new(big.Int).SetUint64(0), l: 1},
{i: new(big.Int).SetUint64(1), l: 1},
{i: new(big.Int).SetUint64(10), l: 2},
{i: new(big.Int).SetUint64(100), l: 3},
{i: new(big.Int).SetUint64(1000), l: 4},
{i: new(big.Int).SetUint64(10000), l: 5},
{i: new(big.Int).SetUint64(100000), l: 6},
{i: new(big.Int).SetUint64(1000000), l: 7},
{i: new(big.Int).SetUint64(10000000), l: 8},
{i: new(big.Int).SetUint64(100000000), l: 9},
{i: new(big.Int).SetUint64(1000000000), l: 10},
{i: new(big.Int).SetUint64(10000000000), l: 11},
{i: new(big.Int).SetUint64(100000000000), l: 12},
{i: new(big.Int).SetUint64(1000000000000), l: 13},
{i: new(big.Int).SetUint64(10000000000000), l: 14},
{i: new(big.Int).SetUint64(100000000000000), l: 15},
{i: new(big.Int).SetUint64(1000000000000000), l: 16},
{i: new(big.Int).SetUint64(10000000000000000), l: 17},
{i: new(big.Int).SetUint64(100000000000000000), l: 18},
{i: new(big.Int).SetUint64(1000000000000000000), l: 19},
{i: new(big.Int).SetUint64(10000000000000000000), l: 20},
{i: BigPow10(25), l: 26},
{i: BigPow10(50), l: 51},
{i: BigPow10(150), l: 151},
{i: BigPow10(2500), l: 2501},
}
for i, v := range tests {
if l := BigLength(v.i); l != v.l {
t.Fatalf("#%d: wanted %d, got %d", i, v.l, l)
}
}
// Test a really long one.
x := new(big.Int).Exp(big.NewInt(10), big.NewInt(1e5), nil)
n := len(x.String())
if l := BigLength(x); l != n {
t.Fatalf("exp(10, 1e5): wanted %d, got %d", n, l)
}
}
var lengths = func() []*big.Int {
const (
N = 1000
S = 64 * 3 // 1 << 18
T = ((N * S) + 7) / 8
Sb = 8
)
var buf [T + 1]byte
if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil {
panic(err)
}
var a [N]*big.Int
for j := 0; j < T; j += (S + 7) / 8 {
chunk := buf[j : j+((S+7)/8)]
chunk[0] &= uint8(int(1<<Sb) - 1)
chunk[0] |= 3 << (Sb - 2)
a[j/((S+7)/8)] = new(big.Int).SetBytes(chunk)
}
return a[:]
}()
var rnd = mrand.New(mrand.NewSource(0))
var smallLengths = func() (a [5000]uint64) {
for i := range a {
a[i] = rnd.Uint64()
}
return a
}()
var gl int
func BenchmarkLength(b *testing.B) {
var ll int
for i := 0; i < b.N; i++ {
ll = Length(smallLengths[i%len(smallLengths)])
}
gl = ll
}
func BenchmarkBigLength(b *testing.B) {
var ll int
for i := 0; i < b.N; i++ {
ll = BigLength(lengths[i%len(lengths)])
}
gl = ll
}

Просмотреть файл

@ -1,206 +0,0 @@
package arith
import (
"math/big"
"sync"
"sync/atomic"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/c"
)
const (
// PowTabLen is the largest cached power for integers.
PowTabLen = 20
// BigPowTabLen is the largest cached power for *big.Ints.
BigPowTabLen = 1e5
)
var (
pow10tab = [PowTabLen]uint64{
0: 1,
1: 10,
2: 100,
3: 1000,
4: 10000,
5: 100000,
6: 1000000,
7: 10000000,
8: 100000000,
9: 1000000000,
10: 10000000000,
11: 100000000000,
12: 1000000000000,
13: 10000000000000,
14: 100000000000000,
15: 1000000000000000,
16: 10000000000000000,
17: 100000000000000000,
18: 1000000000000000000,
19: 10000000000000000000,
}
bigMu sync.Mutex // protects writes to bigPow10Tab
bigPow10Tab atomic.Value
)
func loadBigTable() []*big.Int {
return *(bigPow10Tab.Load().(*[]*big.Int))
}
func storeBigTable(x *[]*big.Int) {
bigPow10Tab.Store(x)
}
// PowOfTenBig reports whether x is a power of 10.
func PowOfTenBig(x *big.Int) bool {
if x.Bit(0) != 0 {
return x.Cmp(c.OneInt) == 0
}
if x.Sign() == 0 {
return true
}
q := new(big.Int).Set(x)
r := new(big.Int)
for len := BigLength(x); len > 20; len-- {
q.QuoRem(q, c.TenInt, r)
if r.Sign() != 0 {
return false
}
}
return PowOfTen(q.Uint64())
}
// PowOfTen reports whether x is a power of 10.
func PowOfTen(x uint64) bool {
if x&1 != 0 {
return x == 1
}
switch x {
case 10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000:
return true
default:
return false
}
}
// BigPow10 computes 10**n.
//
// The returned *big.Int must not be modified.
func BigPow10(n uint64) *big.Int {
tab := loadBigTable()
tabLen := uint64(len(tab))
if n < tabLen {
return tab[n]
}
// Too large for our table.
if n >= BigPowTabLen {
// As an optimization, we don't need to start from
// scratch each time. Start from the largest term we've
// found so far.
partial := tab[tabLen-1]
p := new(big.Int).SetUint64(n - (tabLen - 1))
return p.Mul(partial, p.Exp(c.TenInt, p, nil))
}
return growBigTen(n)
}
func growBigTen(n uint64) *big.Int {
// We need to expand our table to contain the value for
// 10**n.
bigMu.Lock()
tab := loadBigTable()
// Look again in case the table was rebuilt before we grabbed
// the lock.
tableLen := uint64(len(tab))
if n < tableLen {
bigMu.Unlock()
return tab[n]
}
// n < BigTabLen
newLen := tableLen * 2
for newLen <= n {
newLen *= 2
}
if newLen > BigPowTabLen {
newLen = BigPowTabLen
}
for i := tableLen; i < newLen; i++ {
tab = append(tab, new(big.Int).Mul(tab[i-1], c.TenInt))
}
storeBigTable(&tab)
bigMu.Unlock()
return tab[n]
}
func Safe(e uint64) bool {
return e < PowTabLen
}
// Pow10 returns 10**e and a boolean indicating whether the
// result fits into a uint64.
func Pow10(e uint64) (uint64, bool) {
if e < PowTabLen {
return pow10tab[e], true
}
return 0, false
}
// Pow10Int returns 10**e and a boolean indicating whether the
// result fits into an int64.
func Pow10Int(e uint64) (int64, bool) {
if e < PowTabLen-1 {
return int64(pow10tab[e]), true
}
return 0, false
}
func init() {
// Can we move this into a var decl without copylock freaking out?
storeBigTable(&[]*big.Int{
0: new(big.Int).SetUint64(1),
1: c.TenInt,
2: new(big.Int).SetUint64(100),
3: new(big.Int).SetUint64(1000),
4: new(big.Int).SetUint64(10000),
5: new(big.Int).SetUint64(100000),
6: new(big.Int).SetUint64(1000000),
7: new(big.Int).SetUint64(10000000),
8: new(big.Int).SetUint64(100000000),
9: new(big.Int).SetUint64(1000000000),
10: new(big.Int).SetUint64(10000000000),
11: new(big.Int).SetUint64(100000000000),
12: new(big.Int).SetUint64(1000000000000),
13: new(big.Int).SetUint64(10000000000000),
14: new(big.Int).SetUint64(100000000000000),
15: new(big.Int).SetUint64(1000000000000000),
16: new(big.Int).SetUint64(10000000000000000),
17: new(big.Int).SetUint64(100000000000000000),
18: new(big.Int).SetUint64(1000000000000000000),
19: new(big.Int).SetUint64(10000000000000000000),
})
}

Просмотреть файл

@ -1,25 +0,0 @@
// Package c provides internal constants.
package c
import (
"math"
"math/big"
)
const Inflated uint64 = math.MaxUint64
var (
OneInt = big.NewInt(1)
TwoInt = big.NewInt(2)
FiveInt = big.NewInt(5)
TenInt = big.NewInt(10)
OneMillionInt = big.NewInt(1000000)
TenFloat = big.NewFloat(10)
MaxInt64 = big.NewInt(math.MaxInt64)
MinInt64 = big.NewInt(math.MinInt64)
MaxInt32 = big.NewInt(math.MaxInt32)
MinInt32 = big.NewInt(math.MinInt32)
)

Просмотреть файл

@ -1,7 +0,0 @@
//go:build js || 386 || mips || mipsle || arm
// +build js 386 mips mipsle arm
package c
const MaxScale = 425000000
const MaxScaleInf = 1000000001

Просмотреть файл

@ -1,7 +0,0 @@
//go:build ppc64le || ppc64 || amd64p32 || s390x || arm64 || mips64 || mips64le || amd64
// +build ppc64le ppc64 amd64p32 s390x arm64 mips64 mips64le amd64
package c
const MaxScale = 999999999999999999
const MaxScaleInf = 2000000000000000001

Просмотреть файл

@ -1,218 +0,0 @@
package decimal
import (
"math"
"math/big"
"testing"
)
func TestIssue20(t *testing.T) {
x := New(10240000000000, 0)
x.Mul(x, New(976563, 9))
if v, _ := x.Int64(); v != 10000005120 {
t.Fatal("error int64: ", v, x.Int(nil).Int64())
}
}
func TestIssue65(t *testing.T) {
const expected = "999999999000000000000000000000"
r, _ := new(big.Rat).SetString(expected)
r2 := new(Big).SetRat(r).Rat(nil)
if r.Cmp(r2) != 0 {
t.Fatalf("expected %q, got %q", r, r2)
}
}
func TestIssue71(t *testing.T) {
x, _ := new(Big).SetString("-433997231707950814777029946371807573425840064343095193931191306942897586882.200850175108941825587256711340679426793690849230895605323379098449524300541372392806145820741928")
y := New(5, 0)
ctx := Context{RoundingMode: ToZero, Precision: 364}
z := new(Big)
ctx.Quo(z, x, y)
r, _ := new(Big).SetString("-86799446341590162955405989274361514685168012868619038786238261388579517376.4401700350217883651174513422681358853587381698461791210646758196899048601082744785612291641483856")
if z.Cmp(r) != 0 || z.Scale() != r.Scale() {
t.Fatalf(`Quo(%s, %s)
wanted: %s (%d)
got : %s (%d)
`, x, y, r, -r.Scale(), z, -z.Scale())
}
}
func TestIssue72(t *testing.T) {
x, _ := new(Big).SetString("-8.45632792449080367076920780185655231664817924617196338687858969707575095137356626097186102468204972266270655439471710162223657196797091956190618036249568250856310899052975275153410779062120467574000771085625757386351708361318971283364474972153263288762761014798575650687906566E+474")
y, _ := new(Big).SetString("4394389707820271499500265597691058417332780189928068605060129835915231607024733174128123086028964659911263805538968425927408117535552905751413991847682423230507052480632597367974353369255973450023914.06480266537851511912348920528179447782332532576762774258658035423323623047681531444628650113938865866058071268742035039370988065347285125745597527162817805470262344343643075954571122548882320506470664701832116848314413975179616459225485097673077072340532232446317251990415268245406080149594165531067657351225251495644780695372152557650401209918010537469259193951365404947434164664325966741900020673085975334136592934327584453217952431999450960191719318690339387778325911")
z := new(Big)
ctx := Context{Precision: 276}
ctx.Rem(z, x, y)
if !z.IsNaN(+1) {
t.Fatalf(`Rem(%s, %s)
wanted: NaN
got : %s
`, x, y, z)
}
}
func TestIssue99(t *testing.T) {
bad := &Big{compact: 11234567890123457890, exp: -20, precision: 20}
ref := &Big{compact: 2, exp: -1, precision: 1}
cmp := bad.Cmp(ref)
if cmp != -1 {
t.Errorf("expected %s smaller than %s\n", bad.String(), ref.String())
}
cmp = ref.Cmp(bad)
if cmp != 1 {
t.Errorf("expected %s larger than %s\n", ref.String(), bad.String())
}
}
func TestIssue104(t *testing.T) {
x := WithContext(Context128).SetUint64(math.MaxUint64)
y, ok := x.Uint64()
if !ok {
t.Error("failed to convert back")
}
if y != math.MaxUint64 {
t.Errorf("conversion 'succeeded' but the value failed to make the round trip - got %d", y)
}
}
func TestIssue105(t *testing.T) {
var b Big
b.SetString("6190.000000000000")
if _, ok := b.Float64(); !ok {
t.Fatal("6190 should fit in a float just fine")
}
}
func TestIssue114(t *testing.T) {
val := New(-1, 0)
f, ok := val.Float64()
if !ok {
t.Fatal("expected true, got false")
}
if f != -1 {
t.Fatalf("expected -1, got %f", f)
}
}
func TestIssue129(t *testing.T) {
const ys = "3032043016321464119267109897502707536081241662295108925759281083" +
"5908762948729460330525095674778062760636202846843095908778429447" +
"9796814873070054845295002271736741151110471789535774982123036612" +
"1550796316003599954950128589658853017487299129885425412133193933" +
"2498903413531228420735747116119687278947157473738664145261533343" +
"2722170532493633174112130233429772097184909350130374469788301052" +
"3482495638079964143016931353366591300743390994201267050772808522" +
"2187455270795897148327571092934363351270820420433419984534292033" +
"9675739631958189684458858870083031556825078177416742007427966636" +
"0514006572844227668041406273671143700886007701183515021593636919" +
"9207495924756741875118532305644201331724779670102065881743376681" +
"3203206640845218097463669557522703962743327392152581126298069254" +
"1371675752848838153664971001230177808500079048087555874537264894" +
"5157865259620204365998628723043246349215190836616719846461617004" +
"0624913343900552612138426875638012514387540454105288758707095367" +
"5948584994999135084215747465116952738745718450623240269941708511" +
"1366930012340692857027275009242547379294036673865462739988473971" +
"3767679168406015019734720008609931393762820307855221374905055757" +
"2746510383957479894295440342407620814450367153478537997173075792" +
"9072648135598878031235574144702184920386067657206481787776552780" +
"2239635888373911848341450646956855422463406393911413229129783107" +
"0522017864168360550852212880497096745423571929600869430698413665" +
"1045303500743527841031091286949740380378950815031897548721882510" +
"0087851028898152865730649781472706179514641042315466508133319155" +
"3448197158089924374869320322729535800201072851864995814974154852" +
"5092983652452143232513638504502096492324385227003586236182108361" +
"5438379862109314812939144480678690473819818969197631889660418795" +
"2142693741203725348608038844901481302569635930101992656179294873" +
"91111033363129672689273333070"
var x, y, z Big
x.SetInf(false)
y.SetString(ys)
z.Mul(&x, &y)
if !z.IsInf(+1) {
t.Fatalf("expected Inf, got %s", &z)
}
}
func TestIssue70(t *testing.T) {
x, _ := new(Big).SetString("1E+41")
ctx := Context{Precision: 1}
ctx.Log10(x, x)
if x.Cmp(New(40, 0)) != 0 {
t.Fatalf(`Log10(1e+41)
wanted: 40
got : %s
`, x)
}
}
func TestIssue69(t *testing.T) {
maxSqrt := uint64(1e6)
for i := uint64(1); i <= maxSqrt; i++ {
var x Big
x.SetUint64(i * i)
var ctx Context
if v, ok := ctx.Sqrt(&x, &x).Uint64(); !ok || v != i {
t.Fatalf(`Sqrt(%d)
wanted: %d (0)
got : %d (%d)
`, i*i, i, v, -x.Scale())
}
}
}
func TestIssue73(t *testing.T) {
x := New(16, 2)
z := new(Big)
ctx := Context{Precision: 4100}
ctx.Sqrt(z, x)
r := New(4, 1)
if z.Cmp(r) != 0 || z.Scale() != r.Scale() || z.Context.Conditions != r.Context.Conditions {
t.Fatalf(`Sqrt(%d)
wanted: %s (%d) %s
got : %s (%d) %s
`, x, r, -r.Scale(), r.Context.Conditions, z, -z.Scale(), z.Context.Conditions)
}
}
func TestIssue75(t *testing.T) {
x := New(576, 2)
z := new(Big)
ctx := Context{Precision: 2}
ctx.Sqrt(z, x)
r := New(24, 1)
if z.Cmp(r) != 0 || z.Scale() != r.Scale() || z.Context.Conditions != r.Context.Conditions {
t.Fatalf(`Sqrt(%d)
wanted: %s (%d) %s
got : %s (%d) %s
`, x, r, -r.Scale(), r.Context.Conditions, z, -z.Scale(), z.Context.Conditions)
}
}
func TestIssue146(t *testing.T) {
var ctx Context
for i := int64(0); i < 10; i++ {
n := New(i, 1)
firstPow := ctx.Pow(new(Big), n, one.get())
if n.Cmp(firstPow) != 0 {
t.Errorf("%v^1 != %v", n, firstPow)
} else {
t.Logf("%v^1 == %v", n, firstPow)
}
}
}
func TestIssue158(t *testing.T) {
val := New(1, -300)
f, ok := val.Float64()
if !ok {
t.Fatal("expected true, got false")
}
if f != float64(1e300) {
t.Fatalf("expected 1e300, got %f", f)
}
}

Просмотреть файл

@ -1,24 +0,0 @@
// Code generated by "stringer -type OperatingMode"; DO NOT EDIT.
package decimal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[GDA-0]
_ = x[Go-1]
}
const _OperatingMode_name = "GDAGo"
var _OperatingMode_index = [...]uint8{0, 3, 5}
func (i OperatingMode) String() string {
if i >= OperatingMode(len(_OperatingMode_index)-1) {
return "OperatingMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _OperatingMode_name[_OperatingMode_index[i]:_OperatingMode_index[i+1]]
}

Просмотреть файл

@ -1,63 +0,0 @@
// Code generated by "stringer -type Payload -linecomment"; DO NOT EDIT.
package decimal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[absvalue-1]
_ = x[acos-2]
_ = x[addinfinf-3]
_ = x[addition-4]
_ = x[asin-5]
_ = x[atan-6]
_ = x[atan2-7]
_ = x[comparison-8]
_ = x[cos-9]
_ = x[division-10]
_ = x[exp-11]
_ = x[invctxomode-12]
_ = x[invctxpgtu-13]
_ = x[invctxpltz-14]
_ = x[invctxrmode-15]
_ = x[invctxsgtu-16]
_ = x[invctxsltu-17]
_ = x[log-18]
_ = x[log10-19]
_ = x[mul0inf-20]
_ = x[multiplication-21]
_ = x[negation-22]
_ = x[nextminus-23]
_ = x[nextplus-24]
_ = x[quantinf-25]
_ = x[quantization-26]
_ = x[quantminmax-27]
_ = x[quantprec-28]
_ = x[quo00-29]
_ = x[quoinfinf-30]
_ = x[quointprec-31]
_ = x[quorem_-32]
_ = x[quotermexp-33]
_ = x[reduction-34]
_ = x[reminfy-35]
_ = x[remprec-36]
_ = x[remx0-37]
_ = x[sin-38]
_ = x[subinfinf-39]
_ = x[subtraction-40]
}
const _Payload_name = "absolute value of NaNacos with NaN as an operandaddition of infinities with opposing signsaddition with NaN as an operandasin with NaN as an operandatan with NaN as an operandatan2 with NaN as an operandcomparison with NaN as an operandcos with NaN as an operanddivision with NaN as an operandexp with NaN as an operandoperation with an invalid OperatingModeoperation with a precision greater than MaxPrecisionoperation with a precision less than zerooperation with an invalid RoundingModeoperation with a scale greater than MaxScaleoperation with a scale lesser than MinScalelog with NaN as an operandlog10 with NaN as an operandmultiplication of zero with infinitymultiplication with NaN as an operandnegation with NaN as an operandnext-minus with NaN as an operandnext-plus with NaN as an operandquantization of an infinityquantization with NaN as an operandquantization exceeds minimum or maximum scalequantization exceeds working precisiondivision of zero by zerodivision of infinity by infinityresult of integer division was larger than the desired precisioninteger division or remainder has too many digitsdivision with unlimited precision has a non-terminating decimal expansionreduction with NaN as an operandremainder of infinityresult of remainder operation was larger than the desired precisionremainder by zerosin with NaN as an operandsubtraction of infinities with opposing signssubtraction with NaN as an operand"
var _Payload_index = [...]uint16{0, 21, 48, 90, 121, 148, 175, 203, 236, 262, 293, 319, 358, 410, 451, 489, 533, 576, 602, 630, 666, 703, 734, 767, 799, 826, 861, 906, 944, 968, 1000, 1064, 1113, 1186, 1218, 1239, 1306, 1323, 1349, 1394, 1428}
func (i Payload) String() string {
i -= 1
if i >= Payload(len(_Payload_index)-1) {
return "Payload(" + strconv.FormatInt(int64(i+1), 10) + ")"
}
return _Payload_name[_Payload_index[i]:_Payload_index[i+1]]
}

Просмотреть файл

@ -1,30 +0,0 @@
// Code generated by "stringer -type RoundingMode"; DO NOT EDIT.
package decimal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ToNearestEven-0]
_ = x[ToNearestAway-1]
_ = x[ToZero-2]
_ = x[AwayFromZero-3]
_ = x[ToNegativeInf-4]
_ = x[ToPositiveInf-5]
_ = x[ToNearestTowardZero-6]
_ = x[unnecessary-7]
}
const _RoundingMode_name = "ToNearestEvenToNearestAwayToZeroAwayFromZeroToNegativeInfToPositiveInfToNearestTowardZerounnecessary"
var _RoundingMode_index = [...]uint8{0, 13, 26, 32, 44, 57, 70, 89, 100}
func (i RoundingMode) String() string {
if i >= RoundingMode(len(_RoundingMode_index)-1) {
return "RoundingMode(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _RoundingMode_name[_RoundingMode_index[i]:_RoundingMode_index[i+1]]
}

Просмотреть файл

@ -1,394 +0,0 @@
//nolint:errcheck
package decimal
import (
"fmt"
"io"
"math"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/arith"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/c"
)
func (z *Big) scan(r io.ByteScanner) error {
if debug {
defer func() { z.validate() }()
}
// http://speleotrove.com/decimal/daconvs.html#refnumsyn
//
// sign ::= '+' | '-'
// digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' |
// '8' | '9'
// indicator ::= 'e' | 'E'
// digits ::= digit [digit]...
// decimal-part ::= digits '.' [digits] | ['.'] digits
// exponent-part ::= indicator [sign] digits
// infinity ::= 'Infinity' | 'Inf'
// nan ::= 'NaN' [digits] | 'sNaN' [digits]
// numeric-value ::= decimal-part [exponent-part] | infinity
// numeric-string ::= [sign] numeric-value | [sign] nan
//
// We deviate a little by being a tad bit more forgiving. For instance,
// we allow case-insensitive nan and infinity values.
// Sign
neg, err := scanSign(r)
if err != nil {
return err
}
z.form, err = z.scanForm(r)
if err != nil {
switch err {
case ConversionSyntax:
z.form = qnan
z.Context.Conditions |= ConversionSyntax
default:
return err
}
return nil
}
if z.isSpecial() {
if neg {
z.form |= signbit
}
return nil
}
// Mantissa (as a unsigned integer)
if err := z.scanMant(r); err != nil {
switch err {
case io.EOF:
z.form = qnan
return io.ErrUnexpectedEOF
case ConversionSyntax:
z.form = qnan
z.Context.Conditions |= ConversionSyntax
default:
return err
}
return nil
}
// Exponent
if err := z.scanExponent(r); err != nil && err != io.EOF {
switch err {
case Underflow:
z.xflow(MinScale, false, neg)
case Overflow:
z.xflow(MinScale, true, neg)
case ConversionSyntax:
z.form = qnan
z.Context.Conditions |= ConversionSyntax
default:
return err
}
return nil
}
// Adjust for negative values.
if neg {
z.form |= signbit
}
return nil
}
func scanSign(r io.ByteScanner) (bool, error) {
ch, err := r.ReadByte()
if err != nil {
return false, err
}
switch ch {
case '+':
return false, nil
case '-':
return true, nil
default:
return false, r.UnreadByte()
}
}
func (z *Big) scanForm(r io.ByteScanner) (form, error) {
ch, err := r.ReadByte()
if err != nil {
return 0, err
}
if (ch >= '0' && ch <= '9') || ch == '.' {
return finite, r.UnreadByte()
}
signal := false
switch ch {
case 'i', 'I':
return z.scanInfinity(r)
case 'q', 'Q':
// OK
case 's', 'S':
signal = true
case 'n', 'N':
r.UnreadByte()
default:
return 0, ConversionSyntax
}
const (
s = "nan"
)
for i := 0; i < len(s); i++ {
ch, err := r.ReadByte()
if err != nil {
if err == io.EOF {
return 0, io.ErrUnexpectedEOF
}
return 0, err
}
if ch != s[i] && ch != s[i]-('a'-'A') {
return 0, ConversionSyntax
}
}
// Parse payload
for {
ch, err := r.ReadByte()
if err != nil {
if err == io.EOF {
break
}
return 0, err
}
if ch < '0' || ch > '9' {
return 0, ConversionSyntax
}
d := ch - '0'
if d >= 10 {
return 0, ConversionSyntax
}
const cutoff = math.MaxUint64/10 + 1
if z.compact >= cutoff {
return 0, ConversionSyntax
}
z.compact *= 10
if z.compact+uint64(d) < z.compact {
return 0, ConversionSyntax
}
z.compact += uint64(d)
}
if signal {
return snan, nil
}
return qnan, nil
}
func (z *Big) scanInfinity(r io.ByteScanner) (form, error) {
const (
s = "infinity"
)
for i := 1; i < len(s); i++ {
ch, err := r.ReadByte()
if err != nil {
if err != io.EOF {
return 0, err
}
if i == len("inf") {
return inf, nil
}
return 0, io.ErrUnexpectedEOF
}
if ch != s[i] && ch != s[i]-('a'-'A') {
return 0, ConversionSyntax
}
}
return inf, nil
}
func (z *Big) scanMant(r io.ByteScanner) (err error) {
buf := make([]byte, 0, 20)
seenDot := false
dot := 0
big := false
Loop:
for {
ch, err := r.ReadByte()
if err != nil {
if err == io.EOF {
break Loop
}
return err
}
// Common case.
if ch >= '0' && ch <= '9' {
buf = append(buf, ch)
if !big {
d := ch - '0'
if d >= 10 {
return ConversionSyntax
}
const cutoff = math.MaxUint64/10 + 1
if z.compact >= cutoff {
big = true
continue
}
z.compact *= 10
if z.compact+uint64(d) < z.compact {
big = true
continue
}
z.compact += uint64(d)
}
continue
}
switch ch {
case '.':
if seenDot {
// Found two dots.
return ConversionSyntax
}
seenDot = true
dot = len(buf)
case 'e', 'E':
// Hit the exponent: we're done here.
if err := r.UnreadByte(); err != nil {
return err
}
break Loop
default:
return ConversionSyntax
}
}
if big || z.compact == c.Inflated {
z.unscaled.SetString(string(buf), 10)
z.compact = c.Inflated
z.precision = arith.BigLength(&z.unscaled)
} else {
z.precision = arith.Length(z.compact)
}
if seenDot {
z.exp = -(len(buf) - dot)
}
return nil
}
func (z *Big) scanExponent(r io.ByteScanner) error {
ch, err := r.ReadByte()
if err != nil {
if err == io.EOF {
return nil
}
return err
}
switch ch {
case 'e', 'E':
// OK
default:
return ConversionSyntax
}
ch, err = r.ReadByte()
if err != nil {
return err
}
var neg bool
switch ch {
case '+':
// OK
case '-':
neg = true
default:
r.UnreadByte()
}
max := uint64(arith.MaxInt)
if neg {
max++ // -math.MinInt
}
var exp uint64
for {
ch, err := r.ReadByte()
if err != nil {
if err == io.EOF {
break
}
return err
}
if ch < '0' || ch > '9' {
return ConversionSyntax
}
d := ch - '0'
if d >= 10 {
return ConversionSyntax
}
const cutoff = math.MaxUint64/10 + 1
if exp >= cutoff {
return ConversionSyntax
}
exp *= 10
v := exp + uint64(d)
if v < exp || v > max {
if neg {
return Underflow
}
return Overflow
}
exp = v
}
if neg {
z.exp -= int(exp)
} else {
z.exp += int(exp)
}
return nil
}
// byteReader implementation borrowed from math/big/intconv.go
// byteReader is a local wrapper around fmt.ScanState; it implements the
// io.ByteReader interface.
type byteReader struct {
fmt.ScanState
}
func (r byteReader) ReadByte() (byte, error) {
ch, size, err := r.ReadRune()
if size != 1 && err == nil {
err = fmt.Errorf("invalid rune %#U", ch)
}
return byte(ch), err
}
func (r byteReader) UnreadByte() error {
return r.UnreadRune()
}
const nines = "99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999"
func (z *Big) LargestForm(integer, decimal int) {
z.form = finite
z.compact = c.Inflated
z.unscaled.SetString(nines[:integer+decimal], 10)
z.precision = arith.BigLength(&z.unscaled)
z.exp = -decimal
z.norm()
}

Просмотреть файл

@ -1,75 +0,0 @@
package decimal
import (
"math/rand"
"strconv"
"testing"
)
func randString() string {
const chars = "0123456789"
n := rand.Intn(5000)
if n == 0 {
n = 16
}
p := make([]byte, n)
for i := range p {
p[i] = chars[rand.Intn(len(chars))]
}
switch rand.Intn(3) {
case 0:
p = append(p, 'e', '+')
p = strconv.AppendInt(p, int64(rand.Intn(350)), 10)
case 1:
p = append(p, 'e', '-')
p = strconv.AppendInt(p, int64(rand.Intn(350)), 10)
case 2:
if lp := len(p); lp > 0 {
p[rand.Intn(lp)] = '.'
}
}
return string(p)
}
var benchInput = func() (a [4096]struct {
b Big
s string
}) {
for i := range a {
a[i].s = randString()
}
return a
}()
var globOk bool
func BenchmarkBig_SetString(b *testing.B) {
var ok bool
for i := 0; i < b.N; i++ {
m := &benchInput[i%len(benchInput)]
_, ok = m.b.SetString(m.s)
}
globOk = ok
}
func TestLargestForm(t *testing.T) {
var cases = []struct {
a, b int
result string
}{
{1, 1, "9.9"},
{1, 0, "9"},
{10, 10, "9999999999.9999999999"},
{5, 5, "99999.99999"},
{8, 0, "99999999"},
{0, 5, "0.99999"},
}
for _, tc := range cases {
var b Big
b.LargestForm(tc.a, tc.b)
if b.String() != tc.result {
t.Errorf("LargestForm(%d, %d) = %q (expected %q)", tc.a, tc.b, b.String(), tc.result)
}
}
}

Просмотреть файл

@ -1,254 +0,0 @@
package decimal
import (
"math/big"
"strconv"
"sync"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/arith"
cst "vitess.io/vitess/go/vt/vtgate/evalengine/decimal/internal/c"
)
// norm normalizes z to enforce the invariant that z.unscaled is
// only used if the decimal has over 20 digits.
func (z *Big) norm() *Big {
if z.unscaled.IsUint64() {
if v := z.unscaled.Uint64(); v != cst.Inflated {
z.compact = v
z.precision = arith.Length(v)
return z
}
}
z.precision = arith.BigLength(&z.unscaled)
z.compact = cst.Inflated
return z
}
// fix check for overflow, underflow, and clamping.
func (c Context) fix(z *Big) *Big {
//mysql
if z.IsFinite() && z.isZero() {
z.setZero(0, 0)
return z
}
adj := z.adjusted()
if adj > c.emax() {
prec := c.precision()
if z.isZero() {
z.exp = c.emax()
z.Context.Conditions |= Clamped
return z
}
switch m := c.RoundingMode; m {
case ToNearestAway, ToNearestEven, ToNearestTowardZero:
z.SetInf(z.Signbit())
case AwayFromZero:
// OK
case ToZero:
z.exp = c.emax() - prec + 1
case ToPositiveInf, ToNegativeInf:
if m == ToPositiveInf == z.Signbit() {
z.exp = c.emax() - prec + 1
} else {
z.SetInf(false)
}
}
z.Context.Conditions |= Overflow | Inexact | Rounded
return z
}
if adj < c.emin() {
tiny := c.etiny()
if z.isZero() {
if z.exp < tiny {
z.setZero(z.form, tiny)
z.Context.Conditions |= Clamped
}
return z
}
z.Context.Conditions |= Subnormal
if z.exp < tiny {
if c.shiftr(z, uint64(tiny-z.exp)) {
z.compact = 1
}
z.exp = tiny
z.Context.Conditions |= Underflow
if z.isZero() {
z.Context.Conditions |= Clamped
}
}
}
return z
}
// alias returns z if z != x, otherwise a newly-allocated big.Int.
func alias(z, x *big.Int) *big.Int {
if z != x {
// We have to check the first element of their internal slices since
// Big doesn't store a pointer to a big.Int.
zb, xb := z.Bits(), x.Bits()
if cap(zb) > 0 && cap(xb) > 0 && &zb[0:cap(zb)][cap(zb)-1] != &xb[0:cap(xb)][cap(xb)-1] {
return z
}
}
return new(big.Int)
}
// invalidContext reports whether the Context is invalid.
//
// If so, it modifies z appropriately.
func (z *Big) invalidContext(c Context) bool {
switch {
case c.Precision < 0:
z.setNaN(InvalidContext, qnan, invctxpltz)
case c.Precision > UnlimitedPrecision:
z.setNaN(InvalidContext, qnan, invctxpgtu)
case c.RoundingMode >= unnecessary:
z.setNaN(InvalidContext, qnan, invctxrmode)
case c.OperatingMode > Go:
z.setNaN(InvalidContext, qnan, invctxomode)
case c.MaxScale > MaxScale:
z.setNaN(InvalidContext, qnan, invctxsgtu)
case c.MinScale < MinScale:
z.setNaN(InvalidContext, qnan, invctxsltu)
default:
return false
}
return true
}
// copybits can be useful when we want to allocate a big.Int without calling
// new or big.Int.Set. For example:
//
// var x big.Int
// if foo {
// x.SetBits(copybits(y.Bits()))
// }
// ...
//
func copybits(x []big.Word) []big.Word {
z := make([]big.Word, len(x))
copy(z, x)
return z
}
// cmpNorm compares x and y in the range [0.1, 0.999...] and
// reports whether x > y.
func cmpNorm(x uint64, xs int, y uint64, ys int) (ok bool) {
goodx, goody := true, true
// xs, ys > 0, so no overflow
if diff := xs - ys; diff != 0 {
if diff < 0 {
x, goodx = arith.MulPow10(x, uint64(-diff))
} else {
y, goody = arith.MulPow10(y, uint64(diff))
}
}
if goodx {
if goody {
return arith.Cmp(x, y) > 0
}
return false
}
return true
}
// cmpNormBig compares x and y in the range [0.1, 0.999...] and returns true if
// x > y. It uses z as backing storage, provided it does not alias x or y.
func cmpNormBig(z, x *big.Int, xs int, y *big.Int, ys int) (ok bool) {
if xs != ys {
z = alias(alias(z, x), y)
if xs < ys {
x = arith.MulBigPow10(z, x, uint64(ys-xs))
} else {
y = arith.MulBigPow10(z, y, uint64(xs-ys))
}
}
// x and y are non-negative
return x.Cmp(y) > 0
}
// scalex adjusts x by scale.
//
// If scale > 0, x = x * 10^scale, otherwise x = x / 10^-scale.
func scalex(x uint64, scale int) (sx uint64, ok bool) {
if scale > 0 {
if sx, ok = arith.MulPow10(x, uint64(scale)); !ok {
return 0, false
}
return sx, true
}
p, ok := arith.Pow10(uint64(-scale))
if !ok {
return 0, false
}
return x / p, true
}
// bigScalex sets z to the big.Int equivalient of scalex.
func bigScalex(z, x *big.Int, scale int) *big.Int {
if scale > 0 {
return arith.MulBigPow10(z, x, uint64(scale))
}
return z.Quo(x, arith.BigPow10(uint64(-scale)))
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
// getmsd returns the n most significant digits in x.
func getmsd(x *Big, n int) uint64 {
if n < 0 || n > arith.MaxLength {
panic("getmsd: out of range: " + strconv.Itoa(n))
}
r := x.Precision() - n
if r < 0 {
r = 0
}
if x.isCompact() {
p, _ := arith.Pow10(uint64(r))
return x.compact / p
}
// Here, x.Precision >= n since n is in [0, 19] and an
// inflated decimal is >= 1<<64-1.
var w big.Int
w.Quo(&x.unscaled, arith.BigPow10(uint64(r)))
return w.Uint64()
}
var decPool = sync.Pool{
New: func() interface{} {
return new(Big)
},
}
func getDec(ctx Context) *Big {
x := decPool.Get().(*Big)
x.Context = ctx
return x
}
func putDec(x *Big) {
decPool.Put(x)
}

Просмотреть файл

@ -1,6 +0,0 @@
//go:build ddebug
// +build ddebug
package decimal
const debug = true

Просмотреть файл

@ -1,6 +0,0 @@
//go:build !ddebug
// +build !ddebug
package decimal
const debug = false

Просмотреть файл

@ -1,188 +0,0 @@
/*
Copyright 2019 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package evalengine
import (
"testing"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal"
)
func newDecimal(val ...string) *decimal.Big {
dec := decimal.WithContext(decimalContextSQL)
switch len(val) {
case 0:
case 1:
dec.SetString(val[0])
if dec.Context.Conditions != 0 {
panic(dec.Context.Conditions)
}
default:
panic("newDecimal([val]?)")
}
return dec
}
func checkAdd(t *testing.T, left, right, result string) {
t.Helper()
l, r, o := newDecimal(left), newDecimal(right), newDecimal()
o.Add(l, r)
if o.String() != result {
t.Errorf("expected %q + %q = %q\nprocessed: %q + %q = %q", left, right, result, l.String(), r.String(), o.String())
}
}
func checkSub(t *testing.T, left, right, result string) {
t.Helper()
l, r, o := newDecimal(left), newDecimal(right), newDecimal()
o.Sub(l, r)
if o.String() != result {
t.Errorf("expected %q - %q = %q\nprocessed: %q - %q = %q", left, right, result, l.String(), r.String(), o.String())
}
}
func checkMul(t *testing.T, left, right, result string) {
t.Helper()
l, r, o := newDecimal(left), newDecimal(right), newDecimal()
o.Mul(l, r)
if o.String() != result {
t.Errorf("expected %q * %q = %q\nprocessed: %q * %q = %q", left, right, result, l.String(), r.String(), o.String())
}
}
func checkDiv(t *testing.T, left, right, result string, scaleIncr int) {
t.Helper()
l := newDecimal(left)
r := newDecimal(right)
o := newDecimal()
o.Div(l, r, scaleIncr)
if o.String() != result {
t.Errorf("expected %q / %q = %q\nprocessed: %q / %q = %q", left, right, result, l.String(), r.String(), o.String())
}
}
func TestDecimalAdd(t *testing.T) {
checkAdd(t, ".00012345000098765", "123.45", "123.45012345000098765")
checkAdd(t, ".1", ".45", "0.55")
checkAdd(t, "1234500009876.5", ".00012345000098765", "1234500009876.50012345000098765")
checkAdd(t, "9999909999999.5", ".555", "9999910000000.055")
checkAdd(t, "99999999", "1", "100000000")
checkAdd(t, "989999999", "1", "990000000")
checkAdd(t, "999999999", "1", "1000000000")
checkAdd(t, "12345", "123.45", "12468.45")
checkAdd(t, "-12345", "-123.45", "-12468.45")
checkSub(t, "-12345", "123.45", "-12468.45")
checkSub(t, "12345", "-123.45", "12468.45")
}
func TestDecimalSub(t *testing.T) {
checkSub(t, ".00012345000098765", "123.45", "-123.44987654999901235")
checkSub(t, "1234500009876.5", ".00012345000098765", "1234500009876.49987654999901235")
checkSub(t, "9999900000000.5", ".555", "9999899999999.945")
checkSub(t, "1111.5551", "1111.555", "0.0001")
checkSub(t, ".555", ".555", "0")
checkSub(t, "10000000", "1", "9999999")
checkSub(t, "1000001000", ".1", "1000000999.9")
checkSub(t, "1000000000", ".1", "999999999.9")
checkSub(t, "12345", "123.45", "12221.55")
checkSub(t, "-12345", "-123.45", "-12221.55")
checkAdd(t, "-12345", "123.45", "-12221.55")
checkAdd(t, "12345", "-123.45", "12221.55")
checkSub(t, "123.45", "12345", "-12221.55")
checkSub(t, "-123.45", "-12345", "12221.55")
checkAdd(t, "123.45", "-12345", "-12221.55")
checkAdd(t, "-123.45", "12345", "12221.55")
checkAdd(t, "5", "-6.0", "-1.0")
}
func TestDecimalMul(t *testing.T) {
checkMul(t, "12", "10", "120")
checkMul(t, "-123.456", "98765.4321", "-12193185.1853376")
checkMul(t, "-123456000000", "98765432100000", "-12193185185337600000000000")
checkMul(t, "123456", "987654321", "121931851853376")
checkMul(t, "123456", "9876543210", "1219318518533760")
checkMul(t, "123", "0.01", "1.23")
checkMul(t, "123", "0", "0")
}
func TestDecimalDiv(t *testing.T) {
checkDiv(t, "120", "10", "12.000000000", 5)
checkDiv(t, "123", "0.01", "12300.000000000", 5)
checkDiv(t, "120", "100000000000.00000", "0.000000001200000000", 5)
checkDiv(t, "-12193185.1853376", "98765.4321", "-123.456000000000000000", 5)
checkDiv(t, "121931851853376", "987654321", "123456.000000000", 5)
checkDiv(t, "0", "987", "0", 5)
checkDiv(t, "1", "3", "0.333333333", 5)
checkDiv(t, "1.000000000000", "3", "0.333333333333333333", 5)
checkDiv(t, "1", "1", "1.000000000", 5)
checkDiv(t, "0.0123456789012345678912345", "9999999999", "0.000000000001234567890246913578148141", 5)
checkDiv(t, "10.333000000", "12.34500", "0.837019036046982584042122316", 5)
checkDiv(t, "10.000000000060", "2", "5.000000000030000000", 5)
}
func TestDecimalRoundings(t *testing.T) {
defer func() {
decimalContextSQL.RoundingMode = roundingModeArithmetic
}()
// (14620 / 9432456) / (24250 / 9432456)
found := false
a := newEvalInt64(14620)
b := newEvalInt64(24250)
d := newEvalInt64(9432456)
for i := 0; i < 7; i++ {
var ri = decimal.RoundingMode(i)
decimalContextSQL.RoundingMode = ri
var x, y, z, xx EvalResult
_ = decimalDivide(&a, &d, 4, &x)
_ = decimalDivide(&b, &d, 4, &y)
_ = decimalDivide(&x, &y, 4, &z)
aa, _ := newDecimalString("10.333000000")
bb, _ := newDecimalString("12.34500")
aad := newEvalDecimal(aa)
bbd := newEvalDecimal(bb)
_ = decimalDivide(&aad, &bbd, 5, &xx)
for j := 0; j < 7; j++ {
var ok1, ok2 bool
var rj = decimal.RoundingMode(j)
str := string(z.decimal().num.FormatCustom(z.decimal().frac, rj))
if str == "0.60288653" {
ok1 = true
}
str = string(xx.decimal().num.FormatCustom(xx.decimal().num.Precision(), rj))
if str == "0.837019036046982584042122316" {
ok2 = true
}
if ok1 && ok2 {
t.Logf("i=%s j=%s => %v", ri, rj, ok1 && ok2)
found = true
}
}
}
if !found {
t.Fatalf("did not find any valid combinations for arithmetic + format rounding modes")
}
}

Просмотреть файл

@ -30,7 +30,7 @@ import (
"vitess.io/vitess/go/sqltypes"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/evalengine/decimal"
"vitess.io/vitess/go/vt/vtgate/evalengine/internal/decimal"
)
type flag uint16
@ -90,12 +90,10 @@ type (
tuple_ *[]EvalResult //nolint
// decimal_ is the numeric decimal for this result. It may be uninitialized.
// Must not be accessed directly: call EvalResult.decimal() instead.
decimal_ *decimalResult //nolint
}
decimalResult struct {
num decimal.Big
frac int
decimal_ decimal.Decimal //nolint
// length_ is the display length of this eval result; right now this only applies
// to Decimal results, but in the future it may also work for CHAR
length_ int32 //nolint
}
)
@ -167,7 +165,7 @@ func (er *EvalResult) int64() int64 {
return int64(er.numeric_)
}
func (er *EvalResult) decimal() *decimalResult {
func (er *EvalResult) decimal() decimal.Decimal {
er.resolve()
return er.decimal_
}
@ -279,10 +277,11 @@ func (er *EvalResult) setFloat(f float64) {
er.collation_ = collationNumeric
}
func (er *EvalResult) setDecimal(dec *decimalResult) {
func (er *EvalResult) setDecimal(dec decimal.Decimal, frac int32) {
er.type_ = int16(sqltypes.Decimal)
er.decimal_ = dec
er.collation_ = collationNumeric
er.length_ = frac
er.clearFlags(flagIntegerRange)
}
@ -385,11 +384,11 @@ func (er *EvalResult) setValue(v sqltypes.Value) error {
}
er.setFloat(fval)
case v.Type() == sqltypes.Decimal:
dec, err := newDecimalString(v.RawStr())
dec, err := decimal.NewFromMySQL(v.Raw())
if err != nil {
return err
}
er.setDecimal(dec)
er.setDecimal(dec, -dec.Exponent())
default:
er.setRaw(v.Type(), v.Raw(), collations.TypedCollation{})
}
@ -508,7 +507,7 @@ func (er *EvalResult) truthy() boolean {
case sqltypes.Float64, sqltypes.Float32:
return makeboolean(er.float64() != 0.0)
case sqltypes.Decimal:
return makeboolean(!er.decimal().num.IsZero())
return makeboolean(!er.decimal().IsZero())
case sqltypes.VarBinary, sqltypes.VarChar:
return makeboolean(parseStringToFloat(er.string()) != 0.0)
case sqltypes.Tuple:
@ -552,8 +551,7 @@ func (er *EvalResult) toRawBytes() []byte {
case sqltypes.Float64, sqltypes.Float32:
return FormatFloat(sqltypes.Float64, er.float64())
case sqltypes.Decimal:
dec := er.decimal()
return dec.num.FormatCustom(dec.frac, roundingModeFormat)
return er.decimal().FormatMySQL(er.length_)
default:
return er.bytes()
}
@ -587,7 +585,7 @@ func (er *EvalResult) toSQLValue(resultType sqltypes.Type) sqltypes.Value {
return sqltypes.MakeTrusted(resultType, FormatFloat(resultType, er.float64()))
case sqltypes.Decimal:
dec := er.decimal()
return sqltypes.MakeTrusted(resultType, dec.num.FormatCustom(dec.frac, roundingModeFormat))
return sqltypes.MakeTrusted(resultType, dec.FormatMySQL(er.length_))
}
default:
return sqltypes.MakeTrusted(resultType, er.bytes())
@ -743,11 +741,11 @@ func (er *EvalResult) setBindVar1(typ sqltypes.Type, value []byte, collation col
}
er.setFloat(fval)
case sqltypes.Decimal:
dec, err := newDecimalString(string(value))
dec, err := decimal.NewFromMySQL(value)
if err != nil {
throwEvalError(err)
}
er.setDecimal(dec)
er.setDecimal(dec, -dec.Exponent())
case sqltypes.HexNum:
raw, err := parseHexNumber(value)
if err != nil {
@ -836,35 +834,22 @@ func (er *EvalResult) makeFloat() {
}
}
func (er *EvalResult) makeDecimal(m, d int) {
func (er *EvalResult) makeDecimal(m, d int32) {
er.makeNumeric()
var dec *decimalResult
var dec decimal.Decimal
switch er.typeof() {
case sqltypes.Decimal:
dec = er.decimal()
case sqltypes.Float64, sqltypes.Float32:
dec = newDecimalFloat64(er.float64())
dec = decimal.NewFromFloat(er.float64())
case sqltypes.Int64:
dec = newDecimalInt64(er.int64())
dec = decimal.NewFromInt(er.int64())
case sqltypes.Uint64:
dec = newDecimalUint64(er.uint64())
dec = decimal.NewFromUint(er.uint64())
}
var clamp decimal.Big
clamp.Context = decimalContextSQL
clamp.LargestForm(m-d, d)
neg := dec.num.Signbit()
dec.num.SetSignbit(false)
if dec.num.Cmp(&clamp) > 0 {
dec.num = clamp
}
dec.num.SetSignbit(neg)
dec.frac = d
er.setDecimal(dec)
er.setDecimal(dec.Clamp(m-d, d), d)
}
func (er *EvalResult) isHexLiteral() bool {
@ -906,14 +891,12 @@ func (er *EvalResult) makeUnsignedIntegral() {
f := math.Round(er.float64())
er.setUint64(uint64(f))
case sqltypes.Decimal:
dec := er.decimal()
dec.num.Context.RoundingMode = roundingModeIntegerConversion
dec.num.RoundToInt()
if dec.num.Signbit() {
i, _ := dec.num.Int64()
dec := er.decimal().Round(0)
if dec.Sign() < 0 {
i, _ := dec.Int64()
er.setUint64(uint64(i))
} else {
u, _ := dec.num.Uint64()
u, _ := dec.Uint64()
er.setUint64(u)
}
default:
@ -932,10 +915,8 @@ func (er *EvalResult) makeSignedIntegral() {
f := math.Round(er.float64())
er.setInt64(int64(f))
case sqltypes.Decimal:
dec := er.decimal()
dec.num.Context.RoundingMode = roundingModeIntegerConversion
dec.num.RoundToInt()
i, _ := dec.num.Int64()
dec := er.decimal().Round(0)
i, _ := dec.Int64()
er.setInt64(i)
default:
panic("BUG: bad type from makeNumeric")
@ -948,9 +929,8 @@ func (er *EvalResult) negateNumeric() {
case sqltypes.Int8, sqltypes.Int16, sqltypes.Int32, sqltypes.Int64:
i := er.int64()
if er.hasFlag(flagIntegerUdf) {
dec := newDecimalInt64(i)
dec.num.SetSignbit(false)
er.setDecimal(dec)
dec := decimal.NewFromInt(i).NegInPlace()
er.setDecimal(dec, 0)
} else {
er.setInt64(-i)
}
@ -959,33 +939,22 @@ func (er *EvalResult) negateNumeric() {
if er.hasFlag(flagHex) {
er.setFloat(-float64(u))
} else if er.hasFlag(flagIntegerOvf) {
dec := newDecimalUint64(u)
dec.num.SetSignbit(true)
er.setDecimal(dec)
dec := decimal.NewFromUint(u).NegInPlace()
er.setDecimal(dec, 0)
} else {
er.setInt64(-int64(u))
}
case sqltypes.Float32, sqltypes.Float64:
er.setFloat(-er.float64())
case sqltypes.Decimal:
dec := er.decimal()
if !dec.num.IsZero() {
dec.num.SetSignbit(!dec.num.Signbit())
if !er.decimal_.IsZero() {
er.decimal_ = er.decimal_.Neg()
}
}
}
func (er *EvalResult) coerceDecimalToFloat() (float64, bool) {
dec := &er.decimal().num
if f, ok := dec.Float64(); ok {
return f, true
}
// normal form for decimal did not fit in float64, attempt reduction before giving up
var reduced decimal.Big
reduced.Copy(dec)
reduced.Reduce()
return reduced.Float64()
return er.decimal().Float64()
}
func (er *EvalResult) coerceToFloat() (float64, error) {
@ -1004,12 +973,12 @@ func (er *EvalResult) coerceToFloat() (float64, error) {
}
}
func (er *EvalResult) coerceToDecimal() *decimalResult {
func (er *EvalResult) coerceToDecimal() decimal.Decimal {
switch er.typeof() {
case sqltypes.Int64:
return newDecimalInt64(er.int64())
return decimal.NewFromInt(er.int64())
case sqltypes.Uint64:
return newDecimalUint64(er.uint64())
return decimal.NewFromUint(er.uint64())
case sqltypes.Float64:
panic("should never coerce FLOAT64 to DECIMAL")
case sqltypes.Decimal:
@ -1038,11 +1007,6 @@ func newEvalFloat(f float64) (er EvalResult) {
return
}
func newEvalDecimal(dec *decimalResult) (er EvalResult) {
er.setDecimal(dec)
return
}
func newEvalResult(v sqltypes.Value) (er EvalResult, err error) {
err = er.setValue(v)
return

Просмотреть файл

@ -25,6 +25,7 @@ import (
"vitess.io/vitess/go/sqltypes"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/evalengine/internal/decimal"
)
// Cast converts a Value to the target type.
@ -158,7 +159,7 @@ func compareNumeric(v1, v2 *EvalResult) (int, error) {
case sqltypes.Float64:
v1.setFloat(float64(v1.int64()))
case sqltypes.Decimal:
v1.setDecimal(newDecimalInt64(v1.int64()))
v1.setDecimal(decimal.NewFromInt(v1.int64()), 0)
}
case sqltypes.Uint64:
switch v2.typeof() {
@ -170,7 +171,7 @@ func compareNumeric(v1, v2 *EvalResult) (int, error) {
case sqltypes.Float64:
v1.setFloat(float64(v1.uint64()))
case sqltypes.Decimal:
v1.setDecimal(newDecimalUint64(v1.uint64()))
v1.setDecimal(decimal.NewFromUint(v1.uint64()), 0)
}
case sqltypes.Float64:
switch v2.typeof() {
@ -182,7 +183,7 @@ func compareNumeric(v1, v2 *EvalResult) (int, error) {
}
v2.setFloat(float64(v2.uint64()))
case sqltypes.Decimal:
f, ok := v2.decimal().num.Float64()
f, ok := v2.decimal().Float64()
if !ok {
return 0, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "DECIMAL value is out of range")
}
@ -191,11 +192,11 @@ func compareNumeric(v1, v2 *EvalResult) (int, error) {
case sqltypes.Decimal:
switch v2.typeof() {
case sqltypes.Int64:
v2.setDecimal(newDecimalInt64(v2.int64()))
v2.setDecimal(decimal.NewFromInt(v2.int64()), 0)
case sqltypes.Uint64:
v2.setDecimal(newDecimalUint64(v2.uint64()))
v2.setDecimal(decimal.NewFromUint(v2.uint64()), 0)
case sqltypes.Float64:
f, ok := v1.decimal().num.Float64()
f, ok := v1.decimal().Float64()
if !ok {
return 0, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "DECIMAL value is out of range")
}
@ -229,7 +230,7 @@ func compareNumeric(v1, v2 *EvalResult) (int, error) {
return -1, nil
}
case sqltypes.Decimal:
return v1.decimal().num.Cmp(&v2.decimal().num), nil
return v1.decimal().Cmp(v2.decimal()), nil
}
// v1>v2

Просмотреть файл

@ -28,6 +28,7 @@ import (
querypb "vitess.io/vitess/go/vt/proto/query"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/evalengine/internal/decimal"
)
type (
@ -343,11 +344,11 @@ func NewLiteralFloatFromBytes(val []byte) (*Literal, error) {
func NewLiteralDecimalFromBytes(val []byte) (*Literal, error) {
lit := &Literal{}
dec, err := newDecimalString(string(val))
dec, err := decimal.NewFromMySQL(val)
if err != nil {
return nil, err
}
lit.Val.setDecimal(dec)
lit.Val.setDecimal(dec, -dec.Exponent())
return lit, nil
}

Просмотреть файл

@ -185,20 +185,19 @@ func compareAllFloat(args []EvalResult, result *EvalResult, cmp int) {
func compareAllDecimal(args []EvalResult, result *EvalResult, cmp int) {
candidateD := args[0].coerceToDecimal()
maxFrac := candidateD.frac
maxFrac := args[0].length_
for _, arg := range args[1:] {
thisD := arg.coerceToDecimal()
if (cmp < 0) == (thisD.num.Cmp(&candidateD.num) < 0) {
if (cmp < 0) == (thisD.Cmp(candidateD) < 0) {
candidateD = thisD
}
if thisD.frac > maxFrac {
maxFrac = thisD.frac
if arg.length_ > maxFrac {
maxFrac = arg.length_
}
}
candidateD.frac = maxFrac
result.setDecimal(candidateD)
result.setDecimal(candidateD, maxFrac)
}
func compareAllText(args []EvalResult, result *EvalResult, cmp int) {

Просмотреть файл

@ -0,0 +1,45 @@
The MIT License (MIT)
Copyright (c) 2015 Spring, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
- Based on https://github.com/oguzbilgic/fpd, which has the following license:
"""
The MIT License (MIT)
Copyright (c) 2013 Oguz Bilgic
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""

Просмотреть файл

@ -0,0 +1,936 @@
/*
Copyright 2022 The Vitess Authors.
Copyright (c) 2015 Spring, Inc.
Copyright (c) 2013 Oguz Bilgic
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Based on MIT licensed code from https://github.com/shopspring/decimal
// See the LICENSE file in this directory
package decimal
import (
"bytes"
"errors"
"fmt"
"math"
"math/big"
"strconv"
"strings"
"vitess.io/vitess/go/hack"
)
// divisionPrecision is the number of decimal places in the result when it
// doesn't divide exactly.
//
// Example:
//
// d1 := decimal.NewFromFloat(2).div(decimal.NewFromFloat(3))
// d1.String() // output: "0.6666666666666667"
// d2 := decimal.NewFromFloat(2).div(decimal.NewFromFloat(30000))
// d2.String() // output: "0.0000666666666667"
// d3 := decimal.NewFromFloat(20000).div(decimal.NewFromFloat(3))
// d3.String() // output: "6666.6666666666666667"
// decimal.divisionPrecision = 3
// d4 := decimal.NewFromFloat(2).div(decimal.NewFromFloat(3))
// d4.String() // output: "0.667"
//
var divisionPrecision = 16
// Zero constant, to make computations faster.
// Zero should never be compared with == or != directly, please use decimal.Equal or decimal.Cmp instead.
var Zero = New(0, 1)
var zeroInt = big.NewInt(0)
var oneInt = big.NewInt(1)
var fiveInt = big.NewInt(5)
var tenInt = big.NewInt(10)
const powTabLen = 20
var pow10bigtab = [powTabLen]*big.Int{
0: new(big.Int).SetUint64(1),
1: tenInt,
2: new(big.Int).SetUint64(100),
3: new(big.Int).SetUint64(1000),
4: new(big.Int).SetUint64(10000),
5: new(big.Int).SetUint64(100000),
6: new(big.Int).SetUint64(1000000),
7: new(big.Int).SetUint64(10000000),
8: new(big.Int).SetUint64(100000000),
9: new(big.Int).SetUint64(1000000000),
10: new(big.Int).SetUint64(10000000000),
11: new(big.Int).SetUint64(100000000000),
12: new(big.Int).SetUint64(1000000000000),
13: new(big.Int).SetUint64(10000000000000),
14: new(big.Int).SetUint64(100000000000000),
15: new(big.Int).SetUint64(1000000000000000),
16: new(big.Int).SetUint64(10000000000000000),
17: new(big.Int).SetUint64(100000000000000000),
18: new(big.Int).SetUint64(1000000000000000000),
19: new(big.Int).SetUint64(10000000000000000000),
}
var limitsBigTab = [powTabLen]*big.Int{
0: zeroInt,
1: new(big.Int).SetUint64(9),
2: new(big.Int).SetUint64(99),
3: new(big.Int).SetUint64(999),
4: new(big.Int).SetUint64(9999),
5: new(big.Int).SetUint64(99999),
6: new(big.Int).SetUint64(999999),
7: new(big.Int).SetUint64(9999999),
8: new(big.Int).SetUint64(99999999),
9: new(big.Int).SetUint64(999999999),
10: new(big.Int).SetUint64(9999999999),
11: new(big.Int).SetUint64(99999999999),
12: new(big.Int).SetUint64(999999999999),
13: new(big.Int).SetUint64(9999999999999),
14: new(big.Int).SetUint64(99999999999999),
15: new(big.Int).SetUint64(999999999999999),
16: new(big.Int).SetUint64(9999999999999999),
17: new(big.Int).SetUint64(99999999999999999),
18: new(big.Int).SetUint64(999999999999999999),
19: new(big.Int).SetUint64(9999999999999999999),
}
// Decimal represents a fixed-point decimal. It is immutable.
// number = value * 10 ^ exp
type Decimal struct {
value *big.Int
// NOTE(vadim): this must be an int32, because we cast it to float64 during
// calculations. If exp is 64 bit, we might lose precision.
// If we cared about being able to represent every possible decimal, we
// could make exp a *big.Int but it would hurt performance and numbers
// like that are unrealistic.
exp int32
}
// New returns a new fixed-point decimal, value * 10 ^ exp.
func New(value int64, exp int32) Decimal {
return Decimal{
value: big.NewInt(value),
exp: exp,
}
}
// NewFromInt converts a int64 to Decimal.
//
// Example:
//
// NewFromInt(123).String() // output: "123"
// NewFromInt(-10).String() // output: "-10"
func NewFromInt(value int64) Decimal {
return Decimal{
value: big.NewInt(value),
exp: 0,
}
}
func NewFromUint(value uint64) Decimal {
return Decimal{
value: new(big.Int).SetUint64(value),
exp: 0,
}
}
var errOverflow = errors.New("overflow")
func parseDecimal64(s []byte) (Decimal, error) {
const cutoff = math.MaxUint64/10 + 1
var n uint64
var dot = -1
for i, c := range s {
var d byte
switch {
case c == '.':
if dot > -1 {
return Decimal{}, fmt.Errorf("too many .s")
}
dot = i
continue
case '0' <= c && c <= '9':
d = c - '0'
default:
return Decimal{}, fmt.Errorf("unexpected character %q", c)
}
if n >= cutoff {
// n*base overflows
return Decimal{}, errOverflow
}
n *= 10
n1 := n + uint64(d)
if n1 < n {
return Decimal{}, errOverflow
}
n = n1
}
var exp int32
if dot != -1 {
exp = -int32(len(s) - dot - 1)
}
return Decimal{
value: new(big.Int).SetUint64(n),
exp: exp,
}, nil
}
func NewFromMySQL(s []byte) (Decimal, error) {
var original = s
var neg bool
if len(s) > 0 {
switch s[0] {
case '+':
s = s[1:]
case '-':
neg = true
s = s[1:]
}
}
if len(s) == 0 {
return Decimal{}, fmt.Errorf("can't convert %q to decimal: too short", original)
}
if len(s) <= 18 {
dec, err := parseDecimal64(s)
if err == nil {
if neg {
dec.value.Neg(dec.value)
}
return dec, nil
}
if err != errOverflow {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: %v", original, err)
}
}
var intString string
var exp int32
if pIndex := bytes.IndexByte(s, '.'); pIndex >= 0 {
if bytes.IndexByte(s[pIndex+1:], '.') != -1 {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", original)
}
if pIndex+1 < len(s) {
var buf strings.Builder
buf.Grow(len(s))
buf.Write(s[:pIndex])
buf.Write(s[pIndex+1:])
intString = buf.String()
} else {
intString = hack.String(s[:pIndex])
}
exp = -int32(len(s[pIndex+1:]))
} else {
intString = hack.String(s)
}
dValue := new(big.Int)
_, ok := dValue.SetString(intString, 10)
if !ok {
return Decimal{}, fmt.Errorf("can't convert %s to decimal", original)
}
if neg {
dValue.Neg(dValue)
}
return Decimal{value: dValue, exp: exp}, nil
}
// NewFromString returns a new Decimal from a string representation.
// Trailing zeroes are not trimmed.
//
// Example:
//
// d, err := NewFromString("-123.45")
// d2, err := NewFromString(".0001")
// d3, err := NewFromString("1.47000")
//
func NewFromString(value string) (Decimal, error) {
originalInput := value
var intString string
var exp int64
// Check if number is using scientific notation
eIndex := strings.IndexAny(value, "Ee")
if eIndex != -1 {
expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32)
if err != nil {
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
}
return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent is not numeric", value)
}
value = value[:eIndex]
exp = expInt
}
pIndex := -1
vLen := len(value)
for i := 0; i < vLen; i++ {
if value[i] == '.' {
if pIndex > -1 {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
}
pIndex = i
}
}
if pIndex == -1 {
// There is no decimal point, we can just parse the original string as
// an int
intString = value
} else {
if pIndex+1 < vLen {
intString = value[:pIndex] + value[pIndex+1:]
} else {
intString = value[:pIndex]
}
expInt := -len(value[pIndex+1:])
exp += int64(expInt)
}
var dValue *big.Int
// strconv.ParseInt is faster than new(big.Int).SetString so this is just a shortcut for strings we know won't overflow
if len(intString) <= 18 {
parsed64, err := strconv.ParseInt(intString, 10, 64)
if err != nil {
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
}
dValue = big.NewInt(parsed64)
} else {
dValue = new(big.Int)
_, ok := dValue.SetString(intString, 10)
if !ok {
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
}
}
if exp < math.MinInt32 || exp > math.MaxInt32 {
// NOTE(vadim): I doubt a string could realistically be this long
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput)
}
return Decimal{
value: dValue,
exp: int32(exp),
}, nil
}
// RequireFromString returns a new Decimal from a string representation
// or panics if NewFromString would have returned an error.
//
// Example:
//
// d := RequireFromString("-123.45")
// d2 := RequireFromString(".0001")
//
func RequireFromString(value string) Decimal {
dec, err := NewFromString(value)
if err != nil {
panic(err)
}
return dec
}
// NewFromFloat converts a float64 to Decimal.
//
// The converted number will contain the number of significant digits that can be
// represented in a float with reliable roundtrip.
// This is typically 15 digits, but may be more in some cases.
// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.
//
// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.
//
// NOTE: this will panic on NaN, +/-inf
func NewFromFloat(value float64) Decimal {
if value == 0 {
return New(0, 0)
}
dec, err := NewFromMySQL(strconv.AppendFloat(nil, value, 'f', -1, 64))
if err != nil {
panic(err)
}
return dec
}
func NewFromFloat32(value float32) Decimal {
if value == 0 {
return New(0, 0)
}
dec, err := NewFromMySQL(strconv.AppendFloat(nil, float64(value), 'f', -1, 32))
if err != nil {
panic(err)
}
return dec
}
// Copy returns a copy of decimal with the same value and exponent, but a different pointer to value.
func (d Decimal) Copy() Decimal {
d.ensureInitialized()
return Decimal{
value: &(*d.value),
exp: d.exp,
}
}
func bigPow10(n uint64) *big.Int {
if n < powTabLen {
return pow10bigtab[n]
}
// Too large for our table.
// As an optimization, we don't need to start from
// scratch each time. Start from the largest term we've
// found so far.
partial := pow10bigtab[powTabLen-1]
p := new(big.Int).SetUint64(n - (powTabLen - 1))
return p.Mul(partial, p.Exp(tenInt, p, nil))
}
// rescale returns a rescaled version of the decimal. Returned
// decimal may be less precise if the given exponent is bigger
// than the initial exponent of the Decimal.
// NOTE: this will truncate, NOT round
//
// Example:
//
// d := New(12345, -4)
// d2 := d.rescale(-1)
// d3 := d2.rescale(-4)
// println(d1)
// println(d2)
// println(d3)
//
// Output:
//
// 1.2345
// 1.2
// 1.2000
//
func (d Decimal) rescale(exp int32) Decimal {
d.ensureInitialized()
value := new(big.Int).Set(d.value)
if exp > d.exp {
scale := bigPow10(uint64(exp - d.exp))
value = value.Quo(value, scale)
} else if exp < d.exp {
scale := bigPow10(uint64(d.exp - exp))
value = value.Mul(value, scale)
}
return Decimal{value: value, exp: exp}
}
// abs returns the absolute value of the decimal.
func (d Decimal) abs() Decimal {
if d.Sign() >= 0 {
return d
}
d.ensureInitialized()
d2Value := new(big.Int).Abs(d.value)
return Decimal{
value: d2Value,
exp: d.exp,
}
}
// Add returns d + d2.
func (d Decimal) Add(d2 Decimal) Decimal {
rd, rd2 := RescalePair(d, d2)
d3Value := new(big.Int).Add(rd.value, rd2.value)
return Decimal{
value: d3Value,
exp: rd.exp,
}
}
// sub returns d - d2.
func (d Decimal) sub(d2 Decimal) Decimal {
rd, rd2 := RescalePair(d, d2)
d3Value := new(big.Int).Sub(rd.value, rd2.value)
return Decimal{
value: d3Value,
exp: rd.exp,
}
}
func (d Decimal) Sub(d2 Decimal) Decimal {
rd := d.sub(d2)
if rd.value.Sign() == 0 {
rd.exp = 0
}
return rd
}
// Neg returns -d.
func (d Decimal) Neg() Decimal {
d.ensureInitialized()
val := new(big.Int).Neg(d.value)
return Decimal{
value: val,
exp: d.exp,
}
}
func (d Decimal) NegInPlace() Decimal {
d.ensureInitialized()
return Decimal{
value: d.value.Neg(d.value),
exp: d.exp,
}
}
// mul returns d * d2.
func (d Decimal) mul(d2 Decimal) Decimal {
d.ensureInitialized()
d2.ensureInitialized()
expInt64 := int64(d.exp) + int64(d2.exp)
if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 {
// NOTE(vadim): better to panic than give incorrect results, as
// Decimals are usually used for money
panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64))
}
d3Value := new(big.Int).Mul(d.value, d2.value)
return Decimal{
value: d3Value,
exp: int32(expInt64),
}
}
func (d Decimal) Mul(d2 Decimal) Decimal {
if d.Sign() == 0 || d2.Sign() == 0 {
return Zero
}
return d.mul(d2)
}
func roundBase10(digits int32) int32 {
return (digits + 8) / 9
}
func (d Decimal) Div(d2 Decimal, scaleIncr int32) Decimal {
if d.Sign() == 0 {
return Zero
}
s1 := -d.exp
s2 := -d2.exp
fracLeft := roundBase10(s1)
fracRight := roundBase10(s2)
scaleIncr -= fracLeft - s1 + fracRight - s2
if scaleIncr < 0 {
scaleIncr = 0
}
scale := roundBase10(fracLeft+fracRight+scaleIncr) * 9
q, _ := d.quoRem(d2, scale)
return q
}
// div returns d / d2. If it doesn't divide exactly, the result will have
// divisionPrecision digits after the decimal point.
func (d Decimal) div(d2 Decimal) Decimal {
return d.divRound(d2, int32(divisionPrecision))
}
// quoRem does division with remainder
// d.quoRem(d2,precision) returns quotient q and remainder r such that
// d = d2 * q + r, q an integer multiple of 10^(-precision)
// 0 <= r < abs(d2) * 10 ^(-precision) if d>=0
// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0
// Note that precision<0 is allowed as input.
func (d Decimal) quoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
d.ensureInitialized()
d2.ensureInitialized()
if d2.value.Sign() == 0 {
panic("decimal division by 0")
}
scale := -precision
e := int64(d.exp - d2.exp - scale)
if e > math.MaxInt32 || e < math.MinInt32 {
panic("overflow in decimal quoRem")
}
var aa, bb, expo big.Int
var scalerest int32
// d = a 10^ea
// d2 = b 10^eb
if e < 0 {
aa = *d.value
expo.SetInt64(-e)
bb.Exp(tenInt, &expo, nil)
bb.Mul(d2.value, &bb)
scalerest = d.exp
// now aa = a
// bb = b 10^(scale + eb - ea)
} else {
expo.SetInt64(e)
aa.Exp(tenInt, &expo, nil)
aa.Mul(d.value, &aa)
bb = *d2.value
scalerest = scale + d2.exp
// now aa = a ^ (ea - eb - scale)
// bb = b
}
var q, r big.Int
q.QuoRem(&aa, &bb, &r)
dq := Decimal{value: &q, exp: scale}
dr := Decimal{value: &r, exp: scalerest}
return dq, dr
}
// divRound divides and rounds to a given precision
// i.e. to an integer multiple of 10^(-precision)
// for a positive quotient digit 5 is rounded up, away from 0
// if the quotient is negative then digit 5 is rounded down, away from 0
// Note that precision<0 is allowed as input.
func (d Decimal) divRound(d2 Decimal, precision int32) Decimal {
// quoRem already checks initialization
q, r := d.quoRem(d2, precision)
// the actual rounding decision is based on comparing r*10^precision and d2/2
// instead compare 2 r 10 ^precision and d2
var rv2 big.Int
rv2.Abs(r.value)
rv2.Lsh(&rv2, 1)
// now rv2 = abs(r.value) * 2
r2 := Decimal{value: &rv2, exp: r.exp + precision}
// r2 is now 2 * r * 10 ^ precision
var c = r2.Cmp(d2.abs())
if c < 0 {
return q
}
if d.value.Sign()*d2.value.Sign() < 0 {
return q.sub(New(1, -precision))
}
return q.Add(New(1, -precision))
}
// mod returns d % d2.
func (d Decimal) mod(d2 Decimal) Decimal {
quo := d.divRound(d2, -d.exp+1).truncate(0)
return d.sub(d2.mul(quo))
}
func (d Decimal) truncate(precision int32) Decimal {
d.ensureInitialized()
if precision >= 0 && -precision > d.exp {
return d.rescale(-precision)
}
return d
}
// isInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
func (d Decimal) isInteger() bool {
// The most typical case, all decimal with exponent higher or equal 0 can be represented as integer
if d.exp >= 0 {
return true
}
// When the exponent is negative we have to check every number after the decimal place
// If all of them are zeroes, we are sure that given decimal can be represented as an integer
var r big.Int
q := new(big.Int).Set(d.value)
for z := abs(d.exp); z > 0; z-- {
q.QuoRem(q, tenInt, &r)
if r.Cmp(zeroInt) != 0 {
return false
}
}
return true
}
// abs calculates absolute value of any int32. Used for calculating absolute value of decimal's exponent.
func abs(n int32) int32 {
if n < 0 {
return -n
}
return n
}
// Cmp compares the numbers represented by d and d2 and returns:
//
// -1 if d < d2
// 0 if d == d2
// +1 if d > d2
//
func (d Decimal) Cmp(d2 Decimal) int {
d.ensureInitialized()
d2.ensureInitialized()
if d.exp == d2.exp {
return d.value.Cmp(d2.value)
}
rd, rd2 := RescalePair(d, d2)
return rd.value.Cmp(rd2.value)
}
func (d Decimal) CmpAbs(d2 Decimal) int {
d.ensureInitialized()
d2.ensureInitialized()
if d.exp == d2.exp {
return d.value.CmpAbs(d2.value)
}
rd, rd2 := RescalePair(d, d2)
return rd.value.CmpAbs(rd2.value)
}
// Equal returns whether the numbers represented by d and d2 are equal.
func (d Decimal) Equal(d2 Decimal) bool {
return d.Cmp(d2) == 0
}
// Sign returns:
//
// -1 if d < 0
// 0 if d == 0
// +1 if d > 0
//
func (d Decimal) Sign() int {
if d.value == nil {
return 0
}
return d.value.Sign()
}
// IsZero return
//
// true if d == 0
// false if d > 0
// false if d < 0
func (d Decimal) IsZero() bool {
return d.Sign() == 0
}
// Exponent returns the exponent, or scale component of the decimal.
func (d Decimal) Exponent() int32 {
return d.exp
}
func (d Decimal) Int64() (int64, bool) {
scaledD := d.rescale(0)
return scaledD.value.Int64(), scaledD.value.IsInt64()
}
func (d Decimal) Uint64() (uint64, bool) {
scaledD := d.rescale(0)
return scaledD.value.Uint64(), scaledD.value.IsUint64()
}
// Float64 returns the nearest float64 value for d and a bool indicating
// whether f represents d exactly.
func (d Decimal) Float64() (f float64, ok bool) {
f, _ = strconv.ParseFloat(d.String(), 64)
ok = !math.IsInf(f, 0)
return
}
// String returns the string representation of the decimal
// with the fixed point.
//
// Example:
//
// d := New(-12345, -3)
// println(d.String())
//
// Output:
//
// -12.345
//
func (d Decimal) String() string {
return string(d.format(make([]byte, 0, 10), true))
}
// StringFixed returns a rounded fixed-point string with places digits after
// the decimal point.
//
// Example:
//
// NewFromFloat(0).StringFixed(2) // output: "0.00"
// NewFromFloat(0).StringFixed(0) // output: "0"
// NewFromFloat(5.45).StringFixed(0) // output: "5"
// NewFromFloat(5.45).StringFixed(1) // output: "5.5"
// NewFromFloat(5.45).StringFixed(2) // output: "5.45"
// NewFromFloat(5.45).StringFixed(3) // output: "5.450"
// NewFromFloat(545).StringFixed(-1) // output: "550"
//
func (d Decimal) StringFixed(places int32) string {
rounded := d.Round(places)
return string(rounded.format(make([]byte, 0, 10), false))
}
func (d Decimal) StringMySQL() string {
return string(d.format(make([]byte, 0, 10), false))
}
func (d Decimal) FormatMySQL(frac int32) []byte {
rounded := d.Round(frac)
return rounded.format(make([]byte, 0, 10), false)
}
// Round rounds the decimal to places decimal places.
// If places < 0, it will round the integer part to the nearest 10^(-places).
//
// Example:
//
// NewFromFloat(5.45).Round(1).String() // output: "5.5"
// NewFromFloat(545).Round(-1).String() // output: "550"
//
func (d Decimal) Round(places int32) Decimal {
if d.exp == -places {
return d
}
// truncate to places + 1
ret := d.rescale(-places - 1)
// add sign(d) * 0.5
if ret.value.Sign() < 0 {
ret.value.Sub(ret.value, fiveInt)
} else {
ret.value.Add(ret.value, fiveInt)
}
// floor for positive numbers, ceil for negative numbers
_, m := ret.value.DivMod(ret.value, tenInt, new(big.Int))
ret.exp++
if ret.value.Sign() < 0 && m.Cmp(zeroInt) != 0 {
ret.value.Add(ret.value, oneInt)
}
return ret
}
func (d Decimal) format(buf []byte, trimTrailingZeros bool) []byte {
if d.exp >= 0 {
return d.rescale(0).value.Append(buf, 10)
}
rawfmt := d.value.Append(nil, 10)
if d.value.Sign() < 0 {
rawfmt = rawfmt[1:]
buf = append(buf, '-')
}
var fractionalPart []byte
dExpInt := int(d.exp)
if len(rawfmt) > -dExpInt {
buf = append(buf, rawfmt[:len(rawfmt)+dExpInt]...)
fractionalPart = rawfmt[len(rawfmt)+dExpInt:]
} else {
buf = append(buf, '0')
num0s := -dExpInt - len(rawfmt)
fractionalPart = make([]byte, 0, num0s+len(rawfmt))
for z := 0; z < num0s; z++ {
fractionalPart = append(fractionalPart, '0')
}
fractionalPart = append(fractionalPart, rawfmt...)
}
if trimTrailingZeros {
i := len(fractionalPart) - 1
for ; i >= 0; i-- {
if fractionalPart[i] != '0' {
break
}
}
fractionalPart = fractionalPart[:i+1]
}
if len(fractionalPart) > 0 {
buf = append(buf, '.')
buf = append(buf, fractionalPart...)
}
return buf
}
func (d *Decimal) ensureInitialized() {
if d.value == nil {
d.value = new(big.Int)
}
}
// RescalePair rescales two decimals to common exponential value (minimal exp of both decimals)
func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) {
d1.ensureInitialized()
d2.ensureInitialized()
if d1.exp == d2.exp {
return d1, d2
}
baseScale := min(d1.exp, d2.exp)
if baseScale != d1.exp {
return d1.rescale(baseScale), d2
}
return d1, d2.rescale(baseScale)
}
func min(x, y int32) int32 {
if x >= y {
return y
}
return x
}
// largestForm returns the largest decimal that can be represented
// with the given amount of integral and fractional digits
// Example:
// largestForm(1, 1) => 9.9
// largestForm(5, 0) => 99999
// largestForm(0, 5) => 0.99999
func largestForm(integral, fractional int32) Decimal {
// nines is just a very long string of nines; to find the
// largest form of a large decimal, we parse as many nines
// as digits are in the form, then adjust the exponent
// to where the decimal point should be.
const nines = "99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999" +
"99999999999999999999999999999999999999999999999999"
digits := int(integral + fractional)
if digits < len(limitsBigTab) {
return Decimal{value: limitsBigTab[digits], exp: -fractional}
}
if digits < len(nines) {
num, _ := new(big.Int).SetString(nines[:digits], 10)
return Decimal{value: num, exp: -fractional}
}
panic("largestForm: too large")
}
func (d Decimal) Clamp(integral, fractional int32) Decimal {
limit := largestForm(integral, fractional)
if d.CmpAbs(limit) <= 0 {
return d
}
if d.value.Sign() < 0 {
return limit.NegInPlace()
}
return limit
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,296 @@
/*
Copyright 2022 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package decimal
import (
"strings"
"testing"
)
func TestDecimalAdd(t *testing.T) {
for _, tc := range []struct {
lhs, rhs, expected string
}{
{".00012345000098765", "123.45", "123.45012345000098765"},
{".1", ".45", "0.55"},
{"1234500009876.5", ".00012345000098765", "1234500009876.50012345000098765"},
{"9999909999999.5", ".555", "9999910000000.055"},
{"99999999", "1", "100000000"},
{"989999999", "1", "990000000"},
{"999999999", "1", "1000000000"},
{"12345", "123.45", "12468.45"},
{"-12345", "-123.45", "-12468.45"},
{"-12345", "123.45", "-12221.55"},
{"12345", "-123.45", "12221.55"},
{"123.45", "-12345", "-12221.55"},
{"-123.45", "12345", "12221.55"},
{"5", "-6.0", "-1.0"},
} {
left := RequireFromString(tc.lhs)
right := RequireFromString(tc.rhs)
out := left.Add(right)
if out.StringMySQL() != tc.expected {
t.Errorf("expected %q + %q = %q\nprocessed: %q + %q = %q",
tc.lhs, tc.rhs, tc.expected, left.StringMySQL(), right.StringMySQL(), out.StringMySQL())
}
}
}
func TestDecimalSub(t *testing.T) {
for _, tc := range []struct {
lhs, rhs, expected string
}{
{".00012345000098765", "123.45", "-123.44987654999901235"},
{"1234500009876.5", ".00012345000098765", "1234500009876.49987654999901235"},
{"9999900000000.5", ".555", "9999899999999.945"},
{"1111.5551", "1111.555", "0.0001"},
{".555", ".555", "0"},
{"10000000", "1", "9999999"},
{"1000001000", ".1", "1000000999.9"},
{"1000000000", ".1", "999999999.9"},
{"12345", "123.45", "12221.55"},
{"-12345", "-123.45", "-12221.55"},
{"-12345", "123.45", "-12468.45"},
{"12345", "-123.45", "12468.45"},
{"123.45", "12345", "-12221.55"},
{"-123.45", "-12345", "12221.55"},
} {
left := RequireFromString(tc.lhs)
right := RequireFromString(tc.rhs)
out := left.Sub(right)
if out.StringMySQL() != tc.expected {
t.Errorf("expected %q - %q = %q\nprocessed: %q - %q = %q",
tc.lhs, tc.rhs, tc.expected, left.StringMySQL(), right.StringMySQL(), out.StringMySQL())
}
}
}
func TestDecimalMul(t *testing.T) {
for _, tc := range []struct {
lhs, rhs, expected string
}{
{"12", "10", "120"},
{"-123.456", "98765.4321", "-12193185.1853376"},
{"-123456000000", "98765432100000", "-12193185185337600000000000"},
{"123456", "987654321", "121931851853376"},
{"123456", "9876543210", "1219318518533760"},
{"123", "0.01", "1.23"},
{"123", "0", "0"},
} {
left := RequireFromString(tc.lhs)
right := RequireFromString(tc.rhs)
out := left.Mul(right)
if out.StringMySQL() != tc.expected {
t.Errorf("expected %q * %q = %q\nprocessed: %q * %q = %q",
tc.lhs, tc.rhs, tc.expected, left.StringMySQL(), right.StringMySQL(), out.StringMySQL())
}
}
}
func TestDecimalDiv(t *testing.T) {
for _, tc := range []struct {
lhs, rhs, expected string
scaleIncr int32
}{
{"120", "10", "12.000000000", 5},
{"123", "0.01", "12300.000000000", 5},
{"120", "100000000000.00000", "0.000000001200000000", 5},
{"-12193185.1853376", "98765.4321", "-123.456000000000000000", 5},
{"121931851853376", "987654321", "123456.000000000", 5},
{"0", "987", "0", 5},
{"1", "3", "0.333333333", 5},
{"1.000000000000", "3", "0.333333333333333333", 5},
{"1", "1", "1.000000000", 5},
{"0.0123456789012345678912345", "9999999999", "0.000000000001234567890246913578148141", 5},
{"10.333000000", "12.34500", "0.837019036046982584042122316", 5},
{"10.000000000060", "2", "5.000000000030000000", 5},
} {
left := RequireFromString(tc.lhs)
right := RequireFromString(tc.rhs)
out := left.Div(right, tc.scaleIncr)
if out.StringMySQL() != tc.expected {
t.Errorf("expected %q / %q = %q\nprocessed: %q / %q = %q",
tc.lhs, tc.rhs, tc.expected, left.StringMySQL(), right.StringMySQL(), out.StringMySQL())
}
}
}
func TestOpRoundings(t *testing.T) {
t.Run("NestedDivisions", func(t *testing.T) {
const Expected = "0.60288653"
// (14620 / 9432456) / (24250 / 9432456) = 0.60288653
a := NewFromInt(14620)
b := NewFromInt(24250)
d := NewFromInt(9432456)
xx := a.Div(d, 4)
yy := b.Div(d, 4)
zz := xx.Div(yy, 4)
got := string(zz.FormatMySQL(8))
if got != Expected {
t.Fatalf("expected %s got %s", Expected, got)
}
})
t.Run("HighPrecision", func(t *testing.T) {
const Expected = "0.837019036046982584042122316"
// 10.333000000 / 12.34500 = 0.837019036046982584042122316
aa := RequireFromString("10.333000000")
bb := RequireFromString("12.34500")
xx := aa.Div(bb, 5)
got := xx.StringMySQL()
if got != Expected {
t.Fatalf("expected %s got %s", Expected, got)
}
})
}
func TestLargestForm(t *testing.T) {
var cases = []struct {
integral, fractional int32
result string
}{
{1, 1, "9.9"},
{1, 0, "9"},
{10, 10, "9999999999.9999999999"},
{5, 5, "99999.99999"},
{8, 0, "99999999"},
{0, 5, "0.99999"},
}
for _, tc := range cases {
var b = largestForm(tc.integral, tc.fractional)
if b.String() != tc.result {
t.Errorf("LargestForm(%d, %d) = %q (expected %q)", tc.integral, tc.fractional, b.String(), tc.result)
}
}
}
var decimals = []string{
"120", "10", "12.000000000",
"123", "0.01", "12300.000000000",
"120", "100000000000.00000", "0.000000001200000000",
"-12193185.1853376", "98765.4321", "-123.456000000000000000",
"121931851853376", "987654321", "123456.000000000",
"0", "987", "0",
"1", "3", "0.333333333",
"1.000000000000", "3", "0.333333333333333333",
"1", "1", "1.000000000",
"0.0123456789012345678912345", "9999999999", "0.000000000001234567890246913578148141",
"10.333000000", "12.34500", "0.837019036046982584042122316",
"10.000000000060", "2", "5.000000000030000000",
"12", "10", "120",
"-123.456", "98765.4321", "-12193185.1853376",
"-123456000000", "98765432100000", "-12193185185337600000000000",
"123456", "987654321", "121931851853376",
"123456", "9876543210", "1219318518533760",
"123", "0.01", "1.23",
"123", "0", "0",
".00012345000098765", "123.45", "-123.44987654999901235",
"1234500009876.5", ".00012345000098765", "1234500009876.49987654999901235",
"9999900000000.5", ".555", "9999899999999.945",
"1111.5551", "1111.555", "0.0001",
".555", ".555", "0",
"10000000", "1", "9999999",
"1000001000", ".1", "1000000999.9",
"1000000000", ".1", "999999999.9",
"12345", "123.45", "12221.55",
"-12345", "-123.45", "-12221.55",
"-12345", "123.45", "-12468.45",
"12345", "-123.45", "12468.45",
"123.45", "12345", "-12221.55",
"-123.45", "-12345", "12221.55",
".00012345000098765", "123.45", "123.45012345000098765",
".1", ".45", "0.55",
"1234500009876.5", ".00012345000098765", "1234500009876.50012345000098765",
"9999909999999.5", ".555", "9999910000000.055",
"99999999", "1", "100000000",
"989999999", "1", "990000000",
"999999999", "1", "1000000000",
"12345", "123.45", "12468.45",
"-12345", "-123.45", "-12468.45",
"-12345", "123.45", "-12221.55",
"12345", "-123.45", "12221.55",
"123.45", "-12345", "-12221.55",
"-123.45", "12345", "12221.55",
"5", "-6.0", "-1.0",
"", "+", "-",
"0.", "1.",
"99999999999999999999.99999999999999999999",
"999999999999999999",
"111111111111111111",
"111111111111111111",
"-999999999999999999",
"-999999999999999999.",
"999999999999999999.0",
}
func BenchmarkDecimalParsing(b *testing.B) {
b.Run("Naive", func(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
for _, dec := range decimals {
_, _ = NewFromString(dec)
}
}
})
var decimalBytes = make([][]byte, 0, len(decimals))
for _, dec := range decimals {
decimalBytes = append(decimalBytes, []byte(dec))
}
b.Run("MySQL", func(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
for _, dec := range decimalBytes {
_, _ = NewFromMySQL(dec)
}
}
})
}
func TestRoundtrip(t *testing.T) {
for _, in := range decimals {
d, err1 := NewFromString(in)
d2, err2 := NewFromMySQL([]byte(in))
if err1 != nil || err2 != nil {
if err1 != nil && err2 != nil {
continue
}
t.Fatalf("mismatch in errors: %v vs %v", err1, err2)
}
expected := in
if strings.HasPrefix(expected, ".") {
expected = "0" + expected
}
if strings.HasSuffix(expected, ".") {
expected = expected[:len(expected)-1]
}
if d.StringMySQL() != expected {
t.Errorf("roundtrip(1) %q -> %q", expected, d.StringMySQL())
}
if d2.StringMySQL() != expected {
t.Errorf("roundtrip(2) %q -> %q", expected, d2.StringMySQL())
}
}
}

Просмотреть файл

@ -195,7 +195,7 @@ func TestGetReturnType(t *testing.T) {
output querypb.Type
expectedErr error
}{{
input: &sqlparser.FuncExpr{Name: sqlparser.NewColIdent("Abs"), Exprs: sqlparser.SelectExprs{
input: &sqlparser.FuncExpr{Name: sqlparser.NewColIdent("abs"), Exprs: sqlparser.SelectExprs{
&sqlparser.AliasedExpr{
Expr: &sqlparser.ColName{
Name: sqlparser.NewColIdent("A"),
@ -220,7 +220,7 @@ func TestGetReturnType(t *testing.T) {
output: querypb.Type_INT64,
expectedErr: nil,
}, {
input: &sqlparser.FuncExpr{Name: sqlparser.NewColIdent("Abs"), Exprs: sqlparser.SelectExprs{
input: &sqlparser.FuncExpr{Name: sqlparser.NewColIdent("abs"), Exprs: sqlparser.SelectExprs{
&sqlparser.StarExpr{},
}},
expectedErr: fmt.Errorf("cannot evaluate return type for *sqlparser.FuncExpr"),