зеркало из https://github.com/github/vitess-gh.git
evalengine: new decimal implementation
Signed-off-by: Vicent Marti <vmg@strn.cat>
This commit is contained in:
Родитель
9a1adb66bf
Коммит
a9e86b0ea3
|
@ -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":
|
||||
|
|
|
@ -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{_j−1}.
|
||||
// If D_j = 0, set D_j = tiny.
|
||||
// Set C_j = b_j+a_j/C{_j−1}.
|
||||
// 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"),
|
||||
|
|
Загрузка…
Ссылка в новой задаче