Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
755 lines
20 KiB
755 lines
20 KiB
// 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. |
|
// """ |
|
|
|
// Copyright 2015 PingCAP, Inc. |
|
// |
|
// 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, |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
package mysql |
|
|
|
// Decimal implements an arbitrary precision fixed-point decimal. |
|
// |
|
// To use as part of a struct: |
|
// |
|
// type Struct struct { |
|
// Number Decimal |
|
// } |
|
// |
|
// The zero-value of a Decimal is 0, as you would expect. |
|
// |
|
// The best way to create a new Decimal is to use decimal.NewFromString, ex: |
|
// |
|
// n, err := decimal.NewFromString("-123.4567") |
|
// n.String() // output: "-123.4567" |
|
// |
|
// NOTE: this can "only" represent numbers with a maximum of 2^31 digits |
|
// after the decimal point. |
|
|
|
import ( |
|
"database/sql/driver" |
|
"fmt" |
|
"math" |
|
"math/big" |
|
"strconv" |
|
"strings" |
|
) |
|
|
|
// 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.6667" |
|
// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000) |
|
// d2.String() // output: "0.0001" |
|
// 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.6667" |
|
// |
|
const ( |
|
MaxFractionDigits = 30 |
|
DivIncreasePrecision = 4 |
|
) |
|
|
|
// ZeroDecimal is zero constant, to make computations faster. |
|
var ZeroDecimal = NewDecimalFromInt(0, 1) |
|
|
|
var zeroInt = big.NewInt(0) |
|
var oneInt = big.NewInt(1) |
|
var fiveInt = big.NewInt(5) |
|
var tenInt = big.NewInt(10) |
|
|
|
// Decimal represents a fixed-point decimal. It is immutable. |
|
// number = value * 10 ^ exp |
|
type Decimal struct { |
|
value *big.Int |
|
|
|
// 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 |
|
fracDigits int32 // Number of fractional digits for string result. |
|
} |
|
|
|
// ConvertToDecimal converts interface to decimal. |
|
func ConvertToDecimal(value interface{}) (Decimal, error) { |
|
switch v := value.(type) { |
|
case int8: |
|
return NewDecimalFromInt(int64(v), 0), nil |
|
case int16: |
|
return NewDecimalFromInt(int64(v), 0), nil |
|
case int32: |
|
return NewDecimalFromInt(int64(v), 0), nil |
|
case int64: |
|
return NewDecimalFromInt(int64(v), 0), nil |
|
case int: |
|
return NewDecimalFromInt(int64(v), 0), nil |
|
case uint8: |
|
return NewDecimalFromUint(uint64(v), 0), nil |
|
case uint16: |
|
return NewDecimalFromUint(uint64(v), 0), nil |
|
case uint32: |
|
return NewDecimalFromUint(uint64(v), 0), nil |
|
case uint64: |
|
return NewDecimalFromUint(uint64(v), 0), nil |
|
case uint: |
|
return NewDecimalFromUint(uint64(v), 0), nil |
|
case float32: |
|
return NewDecimalFromFloat(float64(v)), nil |
|
case float64: |
|
return NewDecimalFromFloat(float64(v)), nil |
|
case string: |
|
return ParseDecimal(v) |
|
case Decimal: |
|
return v, nil |
|
case Hex: |
|
return NewDecimalFromInt(int64(v.Value), 0), nil |
|
case Bit: |
|
return NewDecimalFromUint(uint64(v.Value), 0), nil |
|
case Enum: |
|
return NewDecimalFromUint(uint64(v.Value), 0), nil |
|
case Set: |
|
return NewDecimalFromUint(uint64(v.Value), 0), nil |
|
default: |
|
return Decimal{}, fmt.Errorf("can't convert %v to decimal", value) |
|
} |
|
} |
|
|
|
// NewDecimalFromInt returns a new fixed-point decimal, value * 10 ^ exp. |
|
func NewDecimalFromInt(value int64, exp int32) Decimal { |
|
return Decimal{ |
|
value: big.NewInt(value), |
|
exp: exp, |
|
fracDigits: fracDigitsDefault(exp), |
|
} |
|
} |
|
|
|
// NewDecimalFromUint returns a new fixed-point decimal, value * 10 ^ exp. |
|
func NewDecimalFromUint(value uint64, exp int32) Decimal { |
|
return Decimal{ |
|
value: big.NewInt(0).SetUint64(value), |
|
exp: exp, |
|
fracDigits: fracDigitsDefault(exp), |
|
} |
|
} |
|
|
|
// ParseDecimal returns a new Decimal from a string representation. |
|
// |
|
// Example: |
|
// |
|
// d, err := ParseDecimal("-123.45") |
|
// d2, err := ParseDecimal(".0001") |
|
// |
|
func ParseDecimal(value string) (Decimal, error) { |
|
var intString string |
|
var exp = int32(0) |
|
|
|
n := strings.IndexAny(value, "eE") |
|
if n > 0 { |
|
// It is scientific notation, like 3.14e10 |
|
expInt, err := strconv.Atoi(value[n+1:]) |
|
if err != nil { |
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal, incorrect exponent", value) |
|
} |
|
value = value[0:n] |
|
exp = int32(expInt) |
|
} |
|
|
|
parts := strings.Split(value, ".") |
|
if len(parts) == 1 { |
|
// There is no decimal point, we can just parse the original string as |
|
// an int. |
|
intString = value |
|
} else if len(parts) == 2 { |
|
intString = parts[0] + parts[1] |
|
expInt := -len(parts[1]) |
|
exp += int32(expInt) |
|
} else { |
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value) |
|
} |
|
|
|
dValue := new(big.Int) |
|
_, ok := dValue.SetString(intString, 10) |
|
if !ok { |
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) |
|
} |
|
|
|
val := Decimal{ |
|
value: dValue, |
|
exp: exp, |
|
fracDigits: fracDigitsDefault(exp), |
|
} |
|
if exp < -MaxFractionDigits { |
|
val = val.rescale(-MaxFractionDigits) |
|
} |
|
return val, nil |
|
} |
|
|
|
// NewDecimalFromFloat converts a float64 to Decimal. |
|
// |
|
// Example: |
|
// |
|
// NewDecimalFromFloat(123.45678901234567).String() // output: "123.4567890123456" |
|
// NewDecimalFromFloat(.00000000000000001).String() // output: "0.00000000000000001" |
|
// |
|
// NOTE: this will panic on NaN, +/-inf. |
|
func NewDecimalFromFloat(value float64) Decimal { |
|
floor := math.Floor(value) |
|
|
|
// fast path, where float is an int. |
|
if floor == value && !math.IsInf(value, 0) { |
|
return NewDecimalFromInt(int64(value), 0) |
|
} |
|
|
|
str := strconv.FormatFloat(value, 'f', -1, 64) |
|
dec, err := ParseDecimal(str) |
|
if err != nil { |
|
panic(err) |
|
} |
|
return dec |
|
} |
|
|
|
// NewDecimalFromFloatWithExponent converts a float64 to Decimal, with an arbitrary |
|
// number of fractional digits. |
|
// |
|
// Example: |
|
// |
|
// NewDecimalFromFloatWithExponent(123.456, -2).String() // output: "123.46" |
|
// |
|
func NewDecimalFromFloatWithExponent(value float64, exp int32) Decimal { |
|
mul := math.Pow(10, -float64(exp)) |
|
floatValue := value * mul |
|
if math.IsNaN(floatValue) || math.IsInf(floatValue, 0) { |
|
panic(fmt.Sprintf("Cannot create a Decimal from %v", floatValue)) |
|
} |
|
dValue := big.NewInt(round(floatValue)) |
|
|
|
return Decimal{ |
|
value: dValue, |
|
exp: exp, |
|
fracDigits: fracDigitsDefault(exp), |
|
} |
|
} |
|
|
|
// 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() |
|
if exp < -MaxFractionDigits-1 { |
|
// Limit the number of digits but we can not call Round here because it is called by Round. |
|
// Limit it to MaxFractionDigits + 1 to make sure the final result is correct. |
|
exp = -MaxFractionDigits - 1 |
|
} |
|
// Must convert exps to float64 before - to prevent overflow. |
|
diff := math.Abs(float64(exp) - float64(d.exp)) |
|
value := new(big.Int).Set(d.value) |
|
|
|
expScale := new(big.Int).Exp(tenInt, big.NewInt(int64(diff)), nil) |
|
if exp > d.exp { |
|
value = value.Quo(value, expScale) |
|
} else if exp < d.exp { |
|
value = value.Mul(value, expScale) |
|
} |
|
return Decimal{ |
|
value: value, |
|
exp: exp, |
|
fracDigits: d.fracDigits, |
|
} |
|
} |
|
|
|
// Abs returns the absolute value of the decimal. |
|
func (d Decimal) Abs() Decimal { |
|
d.ensureInitialized() |
|
d2Value := new(big.Int).Abs(d.value) |
|
return Decimal{ |
|
value: d2Value, |
|
exp: d.exp, |
|
fracDigits: d.fracDigits, |
|
} |
|
} |
|
|
|
// Add returns d + d2. |
|
func (d Decimal) Add(d2 Decimal) Decimal { |
|
baseExp := min(d.exp, d2.exp) |
|
rd := d.rescale(baseExp) |
|
rd2 := d2.rescale(baseExp) |
|
|
|
d3Value := new(big.Int).Add(rd.value, rd2.value) |
|
return Decimal{ |
|
value: d3Value, |
|
exp: baseExp, |
|
fracDigits: fracDigitsPlus(d.fracDigits, d2.fracDigits), |
|
} |
|
} |
|
|
|
// Sub returns d - d2. |
|
func (d Decimal) Sub(d2 Decimal) Decimal { |
|
baseExp := min(d.exp, d2.exp) |
|
rd := d.rescale(baseExp) |
|
rd2 := d2.rescale(baseExp) |
|
|
|
d3Value := new(big.Int).Sub(rd.value, rd2.value) |
|
return Decimal{ |
|
value: d3Value, |
|
exp: baseExp, |
|
fracDigits: fracDigitsPlus(d.fracDigits, d2.fracDigits), |
|
} |
|
} |
|
|
|
// 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 { |
|
// It is better to panic than to 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) |
|
val := Decimal{ |
|
value: d3Value, |
|
exp: int32(expInt64), |
|
fracDigits: fracDigitsMul(d.fracDigits, d2.fracDigits), |
|
} |
|
if val.exp < -(MaxFractionDigits) { |
|
val = val.Round(MaxFractionDigits) |
|
} |
|
return val |
|
} |
|
|
|
// 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 { |
|
// Division is hard, use Rat to do it. |
|
ratNum := d.Rat() |
|
ratDenom := d2.Rat() |
|
|
|
quoRat := big.NewRat(0, 1).Quo(ratNum, ratDenom) |
|
|
|
// Converting from Rat to Decimal inefficiently for now. |
|
ret, err := ParseDecimal(quoRat.FloatString(MaxFractionDigits + 1)) |
|
if err != nil { |
|
panic(err) // This should never happen. |
|
} |
|
// To pass test "2 / 3 * 3 < 2" -> "1". |
|
ret = ret.Truncate(MaxFractionDigits) |
|
ret.fracDigits = fracDigitsDiv(d.fracDigits) |
|
return ret |
|
} |
|
|
|
// 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 { |
|
baseExp := min(d.exp, d2.exp) |
|
rd := d.rescale(baseExp) |
|
rd2 := d2.rescale(baseExp) |
|
|
|
return rd.value.Cmp(rd2.value) |
|
} |
|
|
|
// Equals returns whether the numbers represented by d and d2 are equal. |
|
func (d Decimal) Equals(d2 Decimal) bool { |
|
return d.Cmp(d2) == 0 |
|
} |
|
|
|
// Exponent returns the exponent, or scale component of the decimal. |
|
func (d Decimal) Exponent() int32 { |
|
return d.exp |
|
} |
|
|
|
// FracDigits returns the number of fractional digits of the decimal. |
|
func (d Decimal) FracDigits() int32 { |
|
return d.fracDigits |
|
} |
|
|
|
// IntPart returns the integer component of the decimal. |
|
func (d Decimal) IntPart() int64 { |
|
scaledD := d.rescale(0) |
|
return scaledD.value.Int64() |
|
} |
|
|
|
// Rat returns a rational number representation of the decimal. |
|
func (d Decimal) Rat() *big.Rat { |
|
d.ensureInitialized() |
|
if d.exp <= 0 { |
|
// It must negate after casting to prevent int32 overflow. |
|
denom := new(big.Int).Exp(tenInt, big.NewInt(-int64(d.exp)), nil) |
|
return new(big.Rat).SetFrac(d.value, denom) |
|
} |
|
|
|
mul := new(big.Int).Exp(tenInt, big.NewInt(int64(d.exp)), nil) |
|
num := new(big.Int).Mul(d.value, mul) |
|
return new(big.Rat).SetFrac(num, oneInt) |
|
} |
|
|
|
// Float64 returns the nearest float64 value for d and a bool indicating |
|
// whether f represents d exactly. |
|
// For more details, see the documentation for big.Rat.Float64. |
|
func (d Decimal) Float64() (f float64, exact bool) { |
|
return d.Rat().Float64() |
|
} |
|
|
|
// 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 d.StringFixed(d.fracDigits) |
|
} |
|
|
|
// 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 rounded.string(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 { |
|
// 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) |
|
} |
|
ret.fracDigits = places |
|
return ret |
|
} |
|
|
|
// Floor returns the nearest integer value less than or equal to d. |
|
func (d Decimal) Floor() Decimal { |
|
d.ensureInitialized() |
|
|
|
exp := big.NewInt(10) |
|
|
|
// It must negate after casting to prevent int32 overflow. |
|
exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) |
|
|
|
z := new(big.Int).Div(d.value, exp) |
|
return Decimal{value: z, exp: 0} |
|
} |
|
|
|
// Ceil returns the nearest integer value greater than or equal to d. |
|
func (d Decimal) Ceil() Decimal { |
|
d.ensureInitialized() |
|
|
|
exp := big.NewInt(10) |
|
|
|
// It must negate after casting to prevent int32 overflow. |
|
exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) |
|
|
|
z, m := new(big.Int).DivMod(d.value, exp, new(big.Int)) |
|
if m.Cmp(zeroInt) != 0 { |
|
z.Add(z, oneInt) |
|
} |
|
return Decimal{value: z, exp: 0} |
|
} |
|
|
|
// Truncate truncates off digits from the number, without rounding. |
|
// |
|
// NOTE: precision is the last digit that will not be truncated (must be >= 0). |
|
// |
|
// Example: |
|
// |
|
// decimal.NewFromString("123.456").Truncate(2).String() // "123.45" |
|
// |
|
func (d Decimal) Truncate(precision int32) Decimal { |
|
d.ensureInitialized() |
|
if precision >= 0 && -precision > d.exp { |
|
d = d.rescale(-precision) |
|
} |
|
d.fracDigits = precision |
|
return d |
|
} |
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface. |
|
func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error { |
|
str, err := unquoteIfQuoted(decimalBytes) |
|
if err != nil { |
|
return fmt.Errorf("Error decoding string '%s': %s", decimalBytes, err) |
|
} |
|
|
|
decimal, err := ParseDecimal(str) |
|
*d = decimal |
|
if err != nil { |
|
return fmt.Errorf("Error decoding string '%s': %s", str, err) |
|
} |
|
return nil |
|
} |
|
|
|
// MarshalJSON implements the json.Marshaler interface. |
|
func (d Decimal) MarshalJSON() ([]byte, error) { |
|
str := "\"" + d.String() + "\"" |
|
return []byte(str), nil |
|
} |
|
|
|
// Scan implements the sql.Scanner interface for database deserialization. |
|
func (d *Decimal) Scan(value interface{}) error { |
|
str, err := unquoteIfQuoted(value) |
|
if err != nil { |
|
return err |
|
} |
|
*d, err = ParseDecimal(str) |
|
|
|
return err |
|
} |
|
|
|
// Value implements the driver.Valuer interface for database serialization. |
|
func (d Decimal) Value() (driver.Value, error) { |
|
return d.String(), nil |
|
} |
|
|
|
// BigIntValue returns the *bit.Int value member of decimal. |
|
func (d Decimal) BigIntValue() *big.Int { |
|
return d.value |
|
} |
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface for XML |
|
// deserialization. |
|
func (d *Decimal) UnmarshalText(text []byte) error { |
|
str := string(text) |
|
|
|
dec, err := ParseDecimal(str) |
|
*d = dec |
|
if err != nil { |
|
return fmt.Errorf("Error decoding string '%s': %s", str, err) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// MarshalText implements the encoding.TextMarshaler interface for XML |
|
// serialization. |
|
func (d Decimal) MarshalText() (text []byte, err error) { |
|
return []byte(d.String()), nil |
|
} |
|
|
|
// StringScaled first scales the decimal then calls .String() on it. |
|
// NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead. |
|
func (d Decimal) StringScaled(exp int32) string { |
|
return d.rescale(exp).String() |
|
} |
|
|
|
func (d Decimal) string(trimTrailingZeros bool) string { |
|
if d.exp >= 0 { |
|
return d.rescale(0).value.String() |
|
} |
|
|
|
abs := new(big.Int).Abs(d.value) |
|
str := abs.String() |
|
|
|
var intPart, fractionalPart string |
|
|
|
// this cast to int will cause bugs if d.exp == INT_MIN |
|
// and you are on a 32-bit machine. Won't fix this super-edge case. |
|
dExpInt := int(d.exp) |
|
if len(str) > -dExpInt { |
|
intPart = str[:len(str)+dExpInt] |
|
fractionalPart = str[len(str)+dExpInt:] |
|
} else { |
|
intPart = "0" |
|
|
|
num0s := -dExpInt - len(str) |
|
fractionalPart = strings.Repeat("0", num0s) + str |
|
} |
|
|
|
if trimTrailingZeros { |
|
i := len(fractionalPart) - 1 |
|
for ; i >= 0; i-- { |
|
if fractionalPart[i] != '0' { |
|
break |
|
} |
|
} |
|
fractionalPart = fractionalPart[:i+1] |
|
} |
|
|
|
number := intPart |
|
if len(fractionalPart) > 0 { |
|
number += "." + fractionalPart |
|
} |
|
|
|
if d.value.Sign() < 0 { |
|
return "-" + number |
|
} |
|
|
|
return number |
|
} |
|
|
|
func (d *Decimal) ensureInitialized() { |
|
if d.value == nil { |
|
d.value = new(big.Int) |
|
} |
|
} |
|
|
|
func min(x, y int32) int32 { |
|
if x >= y { |
|
return y |
|
} |
|
return x |
|
} |
|
|
|
func max(x, y int32) int32 { |
|
if x >= y { |
|
return x |
|
} |
|
return y |
|
} |
|
|
|
func round(n float64) int64 { |
|
if n < 0 { |
|
return int64(n - 0.5) |
|
} |
|
return int64(n + 0.5) |
|
} |
|
|
|
func unquoteIfQuoted(value interface{}) (string, error) { |
|
bytes, ok := value.([]byte) |
|
if !ok { |
|
return "", fmt.Errorf("Could not convert value '%+v' to byte array", |
|
value) |
|
} |
|
|
|
// If the amount is quoted, strip the quotes. |
|
if len(bytes) > 2 && bytes[0] == '"' && bytes[len(bytes)-1] == '"' { |
|
bytes = bytes[1 : len(bytes)-1] |
|
} |
|
return string(bytes), nil |
|
} |
|
|
|
func fracDigitsDefault(exp int32) int32 { |
|
if exp < 0 { |
|
return min(MaxFractionDigits, -exp) |
|
} |
|
|
|
return 0 |
|
} |
|
|
|
func fracDigitsPlus(x, y int32) int32 { |
|
return max(x, y) |
|
} |
|
|
|
func fracDigitsDiv(x int32) int32 { |
|
return min(x+DivIncreasePrecision, MaxFractionDigits) |
|
} |
|
|
|
func fracDigitsMul(a, b int32) int32 { |
|
return min(MaxFractionDigits, a+b) |
|
}
|
|
|