summaryrefslogtreecommitdiff
path: root/vendor/github.com/shopspring/decimal
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/shopspring/decimal')
-rw-r--r--vendor/github.com/shopspring/decimal/.travis.yml19
-rw-r--r--vendor/github.com/shopspring/decimal/CHANGELOG.md27
-rw-r--r--vendor/github.com/shopspring/decimal/README.md25
-rw-r--r--vendor/github.com/shopspring/decimal/const.go63
-rw-r--r--vendor/github.com/shopspring/decimal/decimal.go775
5 files changed, 712 insertions, 197 deletions
diff --git a/vendor/github.com/shopspring/decimal/.travis.yml b/vendor/github.com/shopspring/decimal/.travis.yml
deleted file mode 100644
index 6326d40f0..000000000
--- a/vendor/github.com/shopspring/decimal/.travis.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-language: go
-
-arch:
- - amd64
- - ppc64le
-
-go:
- - 1.7.x
- - 1.14.x
- - 1.15.x
- - 1.16.x
- - 1.17.x
- - tip
-
-install:
- - go build .
-
-script:
- - go test -v
diff --git a/vendor/github.com/shopspring/decimal/CHANGELOG.md b/vendor/github.com/shopspring/decimal/CHANGELOG.md
index aea61154b..432d0fd4e 100644
--- a/vendor/github.com/shopspring/decimal/CHANGELOG.md
+++ b/vendor/github.com/shopspring/decimal/CHANGELOG.md
@@ -1,3 +1,30 @@
+## Decimal v1.4.0
+#### BREAKING
+- Drop support for Go version older than 1.10 [#361](https://github.com/shopspring/decimal/pull/361)
+
+#### FEATURES
+- Add implementation of natural logarithm [#339](https://github.com/shopspring/decimal/pull/339) [#357](https://github.com/shopspring/decimal/pull/357)
+- Add improved implementation of power operation [#358](https://github.com/shopspring/decimal/pull/358)
+- Add Compare method which forwards calls to Cmp [#346](https://github.com/shopspring/decimal/pull/346)
+- Add NewFromBigRat constructor [#288](https://github.com/shopspring/decimal/pull/288)
+- Add NewFromUint64 constructor [#352](https://github.com/shopspring/decimal/pull/352)
+
+#### ENHANCEMENTS
+- Migrate to Github Actions [#245](https://github.com/shopspring/decimal/pull/245) [#340](https://github.com/shopspring/decimal/pull/340)
+- Fix examples for RoundDown, RoundFloor, RoundUp, and RoundCeil [#285](https://github.com/shopspring/decimal/pull/285) [#328](https://github.com/shopspring/decimal/pull/328) [#341](https://github.com/shopspring/decimal/pull/341)
+- Use Godoc standard to mark deprecated Equals and StringScaled methods [#342](https://github.com/shopspring/decimal/pull/342)
+- Removed unnecessary min function for RescalePair method [#265](https://github.com/shopspring/decimal/pull/265)
+- Avoid reallocation of initial slice in MarshalBinary (GobEncode) [#355](https://github.com/shopspring/decimal/pull/355)
+- Optimize NumDigits method [#301](https://github.com/shopspring/decimal/pull/301) [#356](https://github.com/shopspring/decimal/pull/356)
+- Optimize BigInt method [#359](https://github.com/shopspring/decimal/pull/359)
+- Support scanning uint64 [#131](https://github.com/shopspring/decimal/pull/131) [#364](https://github.com/shopspring/decimal/pull/364)
+- Add docs section with alternative libraries [#363](https://github.com/shopspring/decimal/pull/363)
+
+#### BUGFIXES
+- Fix incorrect calculation of decimal modulo [#258](https://github.com/shopspring/decimal/pull/258) [#317](https://github.com/shopspring/decimal/pull/317)
+- Allocate new(big.Int) in Copy method to deeply clone it [#278](https://github.com/shopspring/decimal/pull/278)
+- Fix overflow edge case in QuoRem method [#322](https://github.com/shopspring/decimal/pull/322)
+
## Decimal v1.3.1
#### ENHANCEMENTS
diff --git a/vendor/github.com/shopspring/decimal/README.md b/vendor/github.com/shopspring/decimal/README.md
index 2e35df068..318c9df58 100644
--- a/vendor/github.com/shopspring/decimal/README.md
+++ b/vendor/github.com/shopspring/decimal/README.md
@@ -1,6 +1,8 @@
# decimal
-[![Build Status](https://app.travis-ci.com/shopspring/decimal.svg?branch=master)](https://app.travis-ci.com/shopspring/decimal) [![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)
+[![ci](https://github.com/shopspring/decimal/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/shopspring/decimal/actions/workflows/ci.yml)
+[![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal)
+[![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)
Arbitrary-precision fixed-point decimal numbers in go.
@@ -20,7 +22,12 @@ Run `go get github.com/shopspring/decimal`
## Requirements
-Decimal library requires Go version `>=1.7`
+Decimal library requires Go version `>=1.10`
+
+## Documentation
+
+http://godoc.org/github.com/shopspring/decimal
+
## Usage
@@ -57,14 +64,16 @@ func main() {
}
```
-## Documentation
-
-http://godoc.org/github.com/shopspring/decimal
+## Alternative libraries
-## Production Usage
+When working with decimal numbers, you might face problems this library is not perfectly suited for.
+Fortunately, thanks to the wonderful community we have a dozen other libraries that you can choose from.
+Explore other alternatives to find the one that best fits your needs :)
-* [Spring](https://shopspring.com/), since August 14, 2014.
-* If you are using this in production, please let us know!
+* [cockroachdb/apd](https://github.com/cockroachdb/apd) - arbitrary precision, mutable and rich API similar to `big.Int`, more performant than this library
+* [alpacahq/alpacadecimal](https://github.com/alpacahq/alpacadecimal) - high performance, low precision (12 digits), fully compatible API with this library
+* [govalues/decimal](https://github.com/govalues/decimal) - high performance, zero-allocation, low precision (19 digits)
+* [greatcloak/decimal](https://github.com/greatcloak/decimal) - fork focusing on billing and e-commerce web application related use cases, includes out-of-the-box BSON marshaling support
## FAQ
diff --git a/vendor/github.com/shopspring/decimal/const.go b/vendor/github.com/shopspring/decimal/const.go
new file mode 100644
index 000000000..e5d6fa87e
--- /dev/null
+++ b/vendor/github.com/shopspring/decimal/const.go
@@ -0,0 +1,63 @@
+package decimal
+
+import (
+ "strings"
+)
+
+const (
+ strLn10 = "2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286248633409525465082806756666287369098781689482907208325554680843799894826233198528393505308965377732628846163366222287698219886746543667474404243274365155048934314939391479619404400222105101714174800368808401264708068556774321622835522011480466371565912137345074785694768346361679210180644507064800027750268491674655058685693567342067058113642922455440575892572420824131469568901675894025677631135691929203337658714166023010570308963457207544037084746994016826928280848118428931484852494864487192780967627127577539702766860595249671667418348570442250719796500471495105049221477656763693866297697952211071826454973477266242570942932258279850258550978526538320760672631716430950599508780752371033310119785754733154142180842754386359177811705430982748238504564801909561029929182431823752535770975053956518769751037497088869218020518933950723853920514463419726528728696511086257149219884997874887377134568620916705849807828059751193854445009978131146915934666241071846692310107598438319191292230792503747298650929009880391941702654416816335727555703151596113564846546190897042819763365836983716328982174407366009162177850541779276367731145041782137660111010731042397832521894898817597921798666394319523936855916447118246753245630912528778330963604262982153040874560927760726641354787576616262926568298704957954913954918049209069438580790032763017941503117866862092408537949861264933479354871737451675809537088281067452440105892444976479686075120275724181874989395971643105518848195288330746699317814634930000321200327765654130472621883970596794457943468343218395304414844803701305753674262153675579814770458031413637793236291560128185336498466942261465206459942072917119370602444929358037007718981097362533224548366988505528285966192805098447175198503666680874970496982273220244823343097169111136813588418696549323714996941979687803008850408979618598756579894836445212043698216415292987811742973332588607915912510967187510929248475023930572665446276200923068791518135803477701295593646298412366497023355174586195564772461857717369368404676577047874319780573853271810933883496338813069945569399346101090745616033312247949360455361849123333063704751724871276379140924398331810164737823379692265637682071706935846394531616949411701841938119405416449466111274712819705817783293841742231409930022911502362192186723337268385688273533371925103412930705632544426611429765388301822384091026198582888433587455960453004548370789052578473166283701953392231047527564998119228742789713715713228319641003422124210082180679525276689858180956119208391760721080919923461516952599099473782780648128058792731993893453415320185969711021407542282796298237068941764740642225757212455392526179373652434440560595336591539160312524480149313234572453879524389036839236450507881731359711238145323701508413491122324390927681724749607955799151363982881058285740538000653371655553014196332241918087621018204919492651483892"
+)
+
+var (
+ ln10 = newConstApproximation(strLn10)
+)
+
+type constApproximation struct {
+ exact Decimal
+ approximations []Decimal
+}
+
+func newConstApproximation(value string) constApproximation {
+ parts := strings.Split(value, ".")
+ coeff, fractional := parts[0], parts[1]
+
+ coeffLen := len(coeff)
+ maxPrecision := len(fractional)
+
+ var approximations []Decimal
+ for p := 1; p < maxPrecision; p *= 2 {
+ r := RequireFromString(value[:coeffLen+p])
+ approximations = append(approximations, r)
+ }
+
+ return constApproximation{
+ RequireFromString(value),
+ approximations,
+ }
+}
+
+// Returns the smallest approximation available that's at least as precise
+// as the passed precision (places after decimal point), i.e. Floor[ log2(precision) ] + 1
+func (c constApproximation) withPrecision(precision int32) Decimal {
+ i := 0
+
+ if precision >= 1 {
+ i++
+ }
+
+ for precision >= 16 {
+ precision /= 16
+ i += 4
+ }
+
+ for precision >= 2 {
+ precision /= 2
+ i++
+ }
+
+ if i >= len(c.approximations) {
+ return c.exact
+ }
+
+ return c.approximations[i]
+}
diff --git a/vendor/github.com/shopspring/decimal/decimal.go b/vendor/github.com/shopspring/decimal/decimal.go
index 84405ec1c..a37a2301e 100644
--- a/vendor/github.com/shopspring/decimal/decimal.go
+++ b/vendor/github.com/shopspring/decimal/decimal.go
@@ -4,14 +4,14 @@
//
// 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"
+// n, err := decimal.NewFromString("-123.4567")
+// n.String() // output: "-123.4567"
//
// To use Decimal as part of a struct:
//
-// type Struct struct {
-// Number Decimal
-// }
+// type StructName struct {
+// Number Decimal
+// }
//
// Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
package decimal
@@ -32,18 +32,31 @@ import (
//
// 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"
-//
+// 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
+// PowPrecisionNegativeExponent specifies the maximum precision of the result (digits after decimal point)
+// when calculating decimal power. Only used for cases where the exponent is a negative number.
+// This constant applies to Pow, PowInt32 and PowBigInt methods, PowWithPrecision method is not constrained by it.
+//
+// Example:
+//
+// d1, err := decimal.NewFromFloat(15.2).PowInt32(-2)
+// d1.String() // output: "0.0043282548476454"
+//
+// decimal.PowPrecisionNegativeExponent = 24
+// d2, err := decimal.NewFromFloat(15.2).PowInt32(-2)
+// d2.String() // output: "0.004328254847645429362881"
+var PowPrecisionNegativeExponent = 16
+
// MarshalJSONWithoutQuotes should be set to true if you want the decimal to
// be JSON marshaled as a number, instead of as a string.
// WARNING: this is dangerous for decimals with many digits, since many JSON
@@ -91,12 +104,12 @@ func New(value int64, exp int32) Decimal {
}
}
-// NewFromInt converts a int64 to Decimal.
+// NewFromInt converts an int64 to Decimal.
//
// Example:
//
-// NewFromInt(123).String() // output: "123"
-// NewFromInt(-10).String() // output: "-10"
+// NewFromInt(123).String() // output: "123"
+// NewFromInt(-10).String() // output: "-10"
func NewFromInt(value int64) Decimal {
return Decimal{
value: big.NewInt(value),
@@ -104,12 +117,12 @@ func NewFromInt(value int64) Decimal {
}
}
-// NewFromInt32 converts a int32 to Decimal.
+// NewFromInt32 converts an int32 to Decimal.
//
// Example:
//
-// NewFromInt(123).String() // output: "123"
-// NewFromInt(-10).String() // output: "-10"
+// NewFromInt(123).String() // output: "123"
+// NewFromInt(-10).String() // output: "-10"
func NewFromInt32(value int32) Decimal {
return Decimal{
value: big.NewInt(int64(value)),
@@ -117,6 +130,18 @@ func NewFromInt32(value int32) Decimal {
}
}
+// NewFromUint64 converts an uint64 to Decimal.
+//
+// Example:
+//
+// NewFromUint64(123).String() // output: "123"
+func NewFromUint64(value uint64) Decimal {
+ return Decimal{
+ value: new(big.Int).SetUint64(value),
+ exp: 0,
+ }
+}
+
// NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp
func NewFromBigInt(value *big.Int, exp int32) Decimal {
return Decimal{
@@ -125,15 +150,33 @@ func NewFromBigInt(value *big.Int, exp int32) Decimal {
}
}
+// NewFromBigRat returns a new Decimal from a big.Rat. The numerator and
+// denominator are divided and rounded to the given precision.
+//
+// Example:
+//
+// d1 := NewFromBigRat(big.NewRat(0, 1), 0) // output: "0"
+// d2 := NewFromBigRat(big.NewRat(4, 5), 1) // output: "0.8"
+// d3 := NewFromBigRat(big.NewRat(1000, 3), 3) // output: "333.333"
+// d4 := NewFromBigRat(big.NewRat(2, 7), 4) // output: "0.2857"
+func NewFromBigRat(value *big.Rat, precision int32) Decimal {
+ return Decimal{
+ value: new(big.Int).Set(value.Num()),
+ exp: 0,
+ }.DivRound(Decimal{
+ value: new(big.Int).Set(value.Denom()),
+ exp: 0,
+ }, precision)
+}
+
// 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")
-//
+// 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
@@ -211,15 +254,14 @@ func NewFromString(value string) (Decimal, error) {
//
// Example:
//
-// r := regexp.MustCompile("[$,]")
-// d1, err := NewFromFormattedString("$5,125.99", r)
+// r := regexp.MustCompile("[$,]")
+// d1, err := NewFromFormattedString("$5,125.99", r)
//
-// r2 := regexp.MustCompile("[_]")
-// d2, err := NewFromFormattedString("1_000_000", r2)
-//
-// r3 := regexp.MustCompile("[USD\\s]")
-// d3, err := NewFromFormattedString("5000 USD", r3)
+// r2 := regexp.MustCompile("[_]")
+// d2, err := NewFromFormattedString("1_000_000", r2)
//
+// r3 := regexp.MustCompile("[USD\\s]")
+// d3, err := NewFromFormattedString("5000 USD", r3)
func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error) {
parsedValue := replRegexp.ReplaceAllString(value, "")
d, err := NewFromString(parsedValue)
@@ -230,13 +272,12 @@ func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, e
}
// RequireFromString returns a new Decimal from a string representation
-// or panics if NewFromString would have returned an error.
+// or panics if NewFromString had returned an error.
//
// Example:
//
-// d := RequireFromString("-123.45")
-// d2 := RequireFromString(".0001")
-//
+// d := RequireFromString("-123.45")
+// d2 := RequireFromString(".0001")
func RequireFromString(value string) Decimal {
dec, err := NewFromString(value)
if err != nil {
@@ -332,8 +373,7 @@ func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal {
//
// Example:
//
-// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"
-//
+// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"
func NewFromFloatWithExponent(value float64, exp int32) Decimal {
if math.IsNaN(value) || math.IsInf(value, 0) {
panic(fmt.Sprintf("Cannot create a Decimal from %v", value))
@@ -418,7 +458,7 @@ func NewFromFloatWithExponent(value float64, exp int32) Decimal {
func (d Decimal) Copy() Decimal {
d.ensureInitialized()
return Decimal{
- value: &(*d.value),
+ value: new(big.Int).Set(d.value),
exp: d.exp,
}
}
@@ -430,7 +470,7 @@ func (d Decimal) Copy() Decimal {
//
// Example:
//
-// d := New(12345, -4)
+// d := New(12345, -4)
// d2 := d.rescale(-1)
// d3 := d2.rescale(-4)
// println(d1)
@@ -442,7 +482,6 @@ func (d Decimal) Copy() Decimal {
// 1.2345
// 1.2
// 1.2000
-//
func (d Decimal) rescale(exp int32) Decimal {
d.ensureInitialized()
@@ -552,11 +591,13 @@ func (d Decimal) Div(d2 Decimal) Decimal {
return d.DivRound(d2, int32(DivisionPrecision))
}
-// QuoRem does divsion with remainder
+// 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
+//
+// 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()
@@ -565,7 +606,7 @@ func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
panic("decimal division by 0")
}
scale := -precision
- e := int64(d.exp - d2.exp - scale)
+ e := int64(d.exp) - int64(d2.exp) - int64(scale)
if e > math.MaxInt32 || e < math.MinInt32 {
panic("overflow in decimal QuoRem")
}
@@ -599,8 +640,10 @@ func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
// 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
+//
+// 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
@@ -628,24 +671,278 @@ func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal {
// Mod returns d % d2.
func (d Decimal) Mod(d2 Decimal) Decimal {
- quo := d.Div(d2).Truncate(0)
- return d.Sub(d2.Mul(quo))
+ _, r := d.QuoRem(d2, 0)
+ return r
}
-// Pow returns d to the power d2
+// Pow returns d to the power of d2.
+// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point.
+//
+// Pow returns 0 (zero-value of Decimal) instead of error for power operation edge cases, to handle those edge cases use PowWithPrecision
+// Edge cases not handled by Pow:
+// - 0 ** 0 => undefined value
+// - 0 ** y, where y < 0 => infinity
+// - x ** y, where x < 0 and y is non-integer decimal => imaginary value
+//
+// Example:
+//
+// d1 := decimal.NewFromFloat(4.0)
+// d2 := decimal.NewFromFloat(4.0)
+// res1 := d1.Pow(d2)
+// res1.String() // output: "256"
+//
+// d3 := decimal.NewFromFloat(5.0)
+// d4 := decimal.NewFromFloat(5.73)
+// res2 := d3.Pow(d4)
+// res2.String() // output: "10118.08037125"
func (d Decimal) Pow(d2 Decimal) Decimal {
- var temp Decimal
- if d2.IntPart() == 0 {
- return NewFromFloat(1)
+ baseSign := d.Sign()
+ expSign := d2.Sign()
+
+ if baseSign == 0 {
+ if expSign == 0 {
+ return Decimal{}
+ }
+ if expSign == 1 {
+ return Decimal{zeroInt, 0}
+ }
+ if expSign == -1 {
+ return Decimal{}
+ }
+ }
+
+ if expSign == 0 {
+ return Decimal{oneInt, 0}
+ }
+
+ // TODO: optimize extraction of fractional part
+ one := Decimal{oneInt, 0}
+ expIntPart, expFracPart := d2.QuoRem(one, 0)
+
+ if baseSign == -1 && !expFracPart.IsZero() {
+ return Decimal{}
+ }
+
+ intPartPow, _ := d.PowBigInt(expIntPart.value)
+
+ // if exponent is an integer we don't need to calculate d1**frac(d2)
+ if expFracPart.value.Sign() == 0 {
+ return intPartPow
+ }
+
+ // TODO: optimize NumDigits for more performant precision adjustment
+ digitsBase := d.NumDigits()
+ digitsExponent := d2.NumDigits()
+
+ precision := digitsBase
+
+ if digitsExponent > precision {
+ precision += digitsExponent
+ }
+
+ precision += 6
+
+ // Calculate x ** frac(y), where
+ // x ** frac(y) = exp(ln(x ** frac(y)) = exp(ln(x) * frac(y))
+ fracPartPow, err := d.Abs().Ln(-d.exp + int32(precision))
+ if err != nil {
+ return Decimal{}
+ }
+
+ fracPartPow = fracPartPow.Mul(expFracPart)
+
+ fracPartPow, err = fracPartPow.ExpTaylor(-d.exp + int32(precision))
+ if err != nil {
+ return Decimal{}
+ }
+
+ // Join integer and fractional part,
+ // base ** (expBase + expFrac) = base ** expBase * base ** expFrac
+ res := intPartPow.Mul(fracPartPow)
+
+ return res
+}
+
+// PowWithPrecision returns d to the power of d2.
+// Precision parameter specifies minimum precision of the result (digits after decimal point).
+// Returned decimal is not rounded to 'precision' places after decimal point.
+//
+// PowWithPrecision returns error when:
+// - 0 ** 0 => undefined value
+// - 0 ** y, where y < 0 => infinity
+// - x ** y, where x < 0 and y is non-integer decimal => imaginary value
+//
+// Example:
+//
+// d1 := decimal.NewFromFloat(4.0)
+// d2 := decimal.NewFromFloat(4.0)
+// res1, err := d1.PowWithPrecision(d2, 2)
+// res1.String() // output: "256"
+//
+// d3 := decimal.NewFromFloat(5.0)
+// d4 := decimal.NewFromFloat(5.73)
+// res2, err := d3.PowWithPrecision(d4, 5)
+// res2.String() // output: "10118.080371595015625"
+//
+// d5 := decimal.NewFromFloat(-3.0)
+// d6 := decimal.NewFromFloat(-6.0)
+// res3, err := d5.PowWithPrecision(d6, 10)
+// res3.String() // output: "0.0013717421"
+func (d Decimal) PowWithPrecision(d2 Decimal, precision int32) (Decimal, error) {
+ baseSign := d.Sign()
+ expSign := d2.Sign()
+
+ if baseSign == 0 {
+ if expSign == 0 {
+ return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0")
+ }
+ if expSign == 1 {
+ return Decimal{zeroInt, 0}, nil
+ }
+ if expSign == -1 {
+ return Decimal{}, fmt.Errorf("cannot represent infinity value of 0 ** y, where y < 0")
+ }
+ }
+
+ if expSign == 0 {
+ return Decimal{oneInt, 0}, nil
+ }
+
+ // TODO: optimize extraction of fractional part
+ one := Decimal{oneInt, 0}
+ expIntPart, expFracPart := d2.QuoRem(one, 0)
+
+ if baseSign == -1 && !expFracPart.IsZero() {
+ return Decimal{}, fmt.Errorf("cannot represent imaginary value of x ** y, where x < 0 and y is non-integer decimal")
+ }
+
+ intPartPow, _ := d.powBigIntWithPrecision(expIntPart.value, precision)
+
+ // if exponent is an integer we don't need to calculate d1**frac(d2)
+ if expFracPart.value.Sign() == 0 {
+ return intPartPow, nil
+ }
+
+ // TODO: optimize NumDigits for more performant precision adjustment
+ digitsBase := d.NumDigits()
+ digitsExponent := d2.NumDigits()
+
+ if int32(digitsBase) > precision {
+ precision = int32(digitsBase)
+ }
+ if int32(digitsExponent) > precision {
+ precision += int32(digitsExponent)
+ }
+ // increase precision by 10 to compensate for errors in further calculations
+ precision += 10
+
+ // Calculate x ** frac(y), where
+ // x ** frac(y) = exp(ln(x ** frac(y)) = exp(ln(x) * frac(y))
+ fracPartPow, err := d.Abs().Ln(precision)
+ if err != nil {
+ return Decimal{}, err
+ }
+
+ fracPartPow = fracPartPow.Mul(expFracPart)
+
+ fracPartPow, err = fracPartPow.ExpTaylor(precision)
+ if err != nil {
+ return Decimal{}, err
+ }
+
+ // Join integer and fractional part,
+ // base ** (expBase + expFrac) = base ** expBase * base ** expFrac
+ res := intPartPow.Mul(fracPartPow)
+
+ return res, nil
+}
+
+// PowInt32 returns d to the power of exp, where exp is int32.
+// Only returns error when d and exp is 0, thus result is undefined.
+//
+// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point.
+//
+// Example:
+//
+// d1, err := decimal.NewFromFloat(4.0).PowInt32(4)
+// d1.String() // output: "256"
+//
+// d2, err := decimal.NewFromFloat(3.13).PowInt32(5)
+// d2.String() // output: "300.4150512793"
+func (d Decimal) PowInt32(exp int32) (Decimal, error) {
+ if d.IsZero() && exp == 0 {
+ return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0")
+ }
+
+ isExpNeg := exp < 0
+ exp = abs(exp)
+
+ n, result := d, New(1, 0)
+
+ for exp > 0 {
+ if exp%2 == 1 {
+ result = result.Mul(n)
+ }
+ exp /= 2
+
+ if exp > 0 {
+ n = n.Mul(n)
+ }
+ }
+
+ if isExpNeg {
+ return New(1, 0).DivRound(result, int32(PowPrecisionNegativeExponent)), nil
+ }
+
+ return result, nil
+}
+
+// PowBigInt returns d to the power of exp, where exp is big.Int.
+// Only returns error when d and exp is 0, thus result is undefined.
+//
+// When exponent is negative the returned decimal will have maximum precision of PowPrecisionNegativeExponent places after decimal point.
+//
+// Example:
+//
+// d1, err := decimal.NewFromFloat(3.0).PowBigInt(big.NewInt(3))
+// d1.String() // output: "27"
+//
+// d2, err := decimal.NewFromFloat(629.25).PowBigInt(big.NewInt(5))
+// d2.String() // output: "98654323103449.5673828125"
+func (d Decimal) PowBigInt(exp *big.Int) (Decimal, error) {
+ return d.powBigIntWithPrecision(exp, int32(PowPrecisionNegativeExponent))
+}
+
+func (d Decimal) powBigIntWithPrecision(exp *big.Int, precision int32) (Decimal, error) {
+ if d.IsZero() && exp.Sign() == 0 {
+ return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0")
}
- temp = d.Pow(d2.Div(NewFromFloat(2)))
- if d2.IntPart()%2 == 0 {
- return temp.Mul(temp)
+
+ tmpExp := new(big.Int).Set(exp)
+ isExpNeg := exp.Sign() < 0
+
+ if isExpNeg {
+ tmpExp.Abs(tmpExp)
+ }
+
+ n, result := d, New(1, 0)
+
+ for tmpExp.Sign() > 0 {
+ if tmpExp.Bit(0) == 1 {
+ result = result.Mul(n)
+ }
+ tmpExp.Rsh(tmpExp, 1)
+
+ if tmpExp.Sign() > 0 {
+ n = n.Mul(n)
+ }
}
- if d2.IntPart() > 0 {
- return temp.Mul(temp).Mul(d)
+
+ if isExpNeg {
+ return New(1, 0).DivRound(result, precision), nil
}
- return temp.Mul(temp).Div(d)
+
+ return result, nil
}
// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm.
@@ -655,9 +952,8 @@ func (d Decimal) Pow(d2 Decimal) Decimal {
//
// Example:
//
-// NewFromFloat(26.1).ExpHullAbrham(2).String() // output: "220000000000"
-// NewFromFloat(26.1).ExpHullAbrham(20).String() // output: "216314672147.05767284"
-//
+// NewFromFloat(26.1).ExpHullAbrham(2).String() // output: "220000000000"
+// NewFromFloat(26.1).ExpHullAbrham(20).String() // output: "216314672147.05767284"
func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) {
// Algorithm based on Variable precision exponential function.
// ACM Transactions on Mathematical Software by T. E. Hull & A. Abrham.
@@ -747,15 +1043,14 @@ func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) {
//
// Example:
//
-// d, err := NewFromFloat(26.1).ExpTaylor(2).String()
-// d.String() // output: "216314672147.06"
-//
-// NewFromFloat(26.1).ExpTaylor(20).String()
-// d.String() // output: "216314672147.05767284062928674083"
+// d, err := NewFromFloat(26.1).ExpTaylor(2).String()
+// d.String() // output: "216314672147.06"
//
-// NewFromFloat(26.1).ExpTaylor(-10).String()
-// d.String() // output: "220000000000"
+// NewFromFloat(26.1).ExpTaylor(20).String()
+// d.String() // output: "216314672147.05767284062928674083"
//
+// NewFromFloat(26.1).ExpTaylor(-10).String()
+// d.String() // output: "220000000000"
func (d Decimal) ExpTaylor(precision int32) (Decimal, error) {
// Note(mwoss): Implementation can be optimized by exclusively using big.Int API only
if d.IsZero() {
@@ -812,14 +1107,162 @@ func (d Decimal) ExpTaylor(precision int32) (Decimal, error) {
return result, nil
}
+// Ln calculates natural logarithm of d.
+// Precision argument specifies how precise the result must be (number of digits after decimal point).
+// Negative precision is allowed.
+//
+// Example:
+//
+// d1, err := NewFromFloat(13.3).Ln(2)
+// d1.String() // output: "2.59"
+//
+// d2, err := NewFromFloat(579.161).Ln(10)
+// d2.String() // output: "6.3615805046"
+func (d Decimal) Ln(precision int32) (Decimal, error) {
+ // Algorithm based on The Use of Iteration Methods for Approximating the Natural Logarithm,
+ // James F. Epperson, The American Mathematical Monthly, Vol. 96, No. 9, November 1989, pp. 831-835.
+ if d.IsNegative() {
+ return Decimal{}, fmt.Errorf("cannot calculate natural logarithm for negative decimals")
+ }
+
+ if d.IsZero() {
+ return Decimal{}, fmt.Errorf("cannot represent natural logarithm of 0, result: -infinity")
+ }
+
+ calcPrecision := precision + 2
+ z := d.Copy()
+
+ var comp1, comp3, comp2, comp4, reduceAdjust Decimal
+ comp1 = z.Sub(Decimal{oneInt, 0})
+ comp3 = Decimal{oneInt, -1}
+
+ // for decimal in range [0.9, 1.1] where ln(d) is close to 0
+ usePowerSeries := false
+
+ if comp1.Abs().Cmp(comp3) <= 0 {
+ usePowerSeries = true
+ } else {
+ // reduce input decimal to range [0.1, 1)
+ expDelta := int32(z.NumDigits()) + z.exp
+ z.exp -= expDelta
+
+ // Input decimal was reduced by factor of 10^expDelta, thus we will need to add
+ // ln(10^expDelta) = expDelta * ln(10)
+ // to the result to compensate that
+ ln10 := ln10.withPrecision(calcPrecision)
+ reduceAdjust = NewFromInt32(expDelta)
+ reduceAdjust = reduceAdjust.Mul(ln10)
+
+ comp1 = z.Sub(Decimal{oneInt, 0})
+
+ if comp1.Abs().Cmp(comp3) <= 0 {
+ usePowerSeries = true
+ } else {
+ // initial estimate using floats
+ zFloat := z.InexactFloat64()
+ comp1 = NewFromFloat(math.Log(zFloat))
+ }
+ }
+
+ epsilon := Decimal{oneInt, -calcPrecision}
+
+ if usePowerSeries {
+ // Power Series - https://en.wikipedia.org/wiki/Logarithm#Power_series
+ // Calculating n-th term of formula: ln(z+1) = 2 sum [ 1 / (2n+1) * (z / (z+2))^(2n+1) ]
+ // until the difference between current and next term is smaller than epsilon.
+ // Coverage quite fast for decimals close to 1.0
+
+ // z + 2
+ comp2 = comp1.Add(Decimal{twoInt, 0})
+ // z / (z + 2)
+ comp3 = comp1.DivRound(comp2, calcPrecision)
+ // 2 * (z / (z + 2))
+ comp1 = comp3.Add(comp3)
+ comp2 = comp1.Copy()
+
+ for n := 1; ; n++ {
+ // 2 * (z / (z+2))^(2n+1)
+ comp2 = comp2.Mul(comp3).Mul(comp3)
+
+ // 1 / (2n+1) * 2 * (z / (z+2))^(2n+1)
+ comp4 = NewFromInt(int64(2*n + 1))
+ comp4 = comp2.DivRound(comp4, calcPrecision)
+
+ // comp1 = 2 sum [ 1 / (2n+1) * (z / (z+2))^(2n+1) ]
+ comp1 = comp1.Add(comp4)
+
+ if comp4.Abs().Cmp(epsilon) <= 0 {
+ break
+ }
+ }
+ } else {
+ // Halley's Iteration.
+ // Calculating n-th term of formula: a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z),
+ // until the difference between current and next term is smaller than epsilon
+ var prevStep Decimal
+ maxIters := calcPrecision*2 + 10
+
+ for i := int32(0); i < maxIters; i++ {
+ // exp(a_n)
+ comp3, _ = comp1.ExpTaylor(calcPrecision)
+ // exp(a_n) - z
+ comp2 = comp3.Sub(z)
+ // 2 * (exp(a_n) - z)
+ comp2 = comp2.Add(comp2)
+ // exp(a_n) + z
+ comp4 = comp3.Add(z)
+ // 2 * (exp(a_n) - z) / (exp(a_n) + z)
+ comp3 = comp2.DivRound(comp4, calcPrecision)
+ // comp1 = a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z)
+ comp1 = comp1.Sub(comp3)
+
+ if prevStep.Add(comp3).IsZero() {
+ // If iteration steps oscillate we should return early and prevent an infinity loop
+ // NOTE(mwoss): This should be quite a rare case, returning error is not necessary
+ break
+ }
+
+ if comp3.Abs().Cmp(epsilon) <= 0 {
+ break
+ }
+
+ prevStep = comp3
+ }
+ }
+
+ comp1 = comp1.Add(reduceAdjust)
+
+ return comp1.Round(precision), nil
+}
+
// NumDigits returns the number of digits of the decimal coefficient (d.Value)
-// Note: Current implementation is extremely slow for large decimals and/or decimals with large fractional part
func (d Decimal) NumDigits() int {
- // Note(mwoss): It can be optimized, unnecessary cast of big.Int to string
- if d.IsNegative() {
- return len(d.value.String()) - 1
+ if d.value == nil {
+ return 1
+ }
+
+ if d.value.IsInt64() {
+ i64 := d.value.Int64()
+ // restrict fast path to integers with exact conversion to float64
+ if i64 <= (1<<53) && i64 >= -(1<<53) {
+ if i64 == 0 {
+ return 1
+ }
+ return int(math.Log10(math.Abs(float64(i64)))) + 1
+ }
+ }
+
+ estimatedNumDigits := int(float64(d.value.BitLen()) / math.Log2(10))
+
+ // estimatedNumDigits (lg10) may be off by 1, need to verify
+ digitsBigInt := big.NewInt(int64(estimatedNumDigits))
+ errorCorrectionUnit := digitsBigInt.Exp(tenInt, digitsBigInt, nil)
+
+ if d.value.CmpAbs(errorCorrectionUnit) >= 0 {
+ return estimatedNumDigits + 1
}
- return len(d.value.String())
+
+ return estimatedNumDigits
}
// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
@@ -851,10 +1294,9 @@ func abs(n int32) int32 {
// Cmp compares the numbers represented by d and d2 and returns:
//
-// -1 if d < d2
-// 0 if d == d2
-// +1 if d > d2
-//
+// -1 if d < d2
+// 0 if d == d2
+// +1 if d > d2
func (d Decimal) Cmp(d2 Decimal) int {
d.ensureInitialized()
d2.ensureInitialized()
@@ -868,12 +1310,21 @@ func (d Decimal) Cmp(d2 Decimal) int {
return rd.value.Cmp(rd2.value)
}
+// Compare 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) Compare(d2 Decimal) int {
+ return d.Cmp(d2)
+}
+
// Equal returns whether the numbers represented by d and d2 are equal.
func (d Decimal) Equal(d2 Decimal) bool {
return d.Cmp(d2) == 0
}
-// Equals is deprecated, please use Equal method instead
+// Deprecated: Equals is deprecated, please use Equal method instead.
func (d Decimal) Equals(d2 Decimal) bool {
return d.Equal(d2)
}
@@ -905,7 +1356,6 @@ func (d Decimal) LessThanOrEqual(d2 Decimal) bool {
// -1 if d < 0
// 0 if d == 0
// +1 if d > 0
-//
func (d Decimal) Sign() int {
if d.value == nil {
return 0
@@ -968,9 +1418,7 @@ func (d Decimal) IntPart() int64 {
// BigInt returns integer component of the decimal as a BigInt.
func (d Decimal) BigInt() *big.Int {
scaledD := d.rescale(0)
- i := &big.Int{}
- i.SetString(scaledD.String(), 10)
- return i
+ return scaledD.value
}
// BigFloat returns decimal as BigFloat.
@@ -1014,13 +1462,12 @@ func (d Decimal) InexactFloat64() float64 {
//
// Example:
//
-// d := New(-12345, -3)
-// println(d.String())
+// d := New(-12345, -3)
+// println(d.String())
//
// Output:
//
-// -12.345
-//
+// -12.345
func (d Decimal) String() string {
return d.string(true)
}
@@ -1030,14 +1477,13 @@ func (d Decimal) String() string {
//
// 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"
-//
+// 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)
@@ -1048,14 +1494,13 @@ func (d Decimal) StringFixed(places int32) string {
//
// Example:
//
-// NewFromFloat(0).StringFixedBank(2) // output: "0.00"
-// NewFromFloat(0).StringFixedBank(0) // output: "0"
-// NewFromFloat(5.45).StringFixedBank(0) // output: "5"
-// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4"
-// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45"
-// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450"
-// NewFromFloat(545).StringFixedBank(-1) // output: "540"
-//
+// NewFromFloat(0).StringFixedBank(2) // output: "0.00"
+// NewFromFloat(0).StringFixedBank(0) // output: "0"
+// NewFromFloat(5.45).StringFixedBank(0) // output: "5"
+// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4"
+// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45"
+// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450"
+// NewFromFloat(545).StringFixedBank(-1) // output: "540"
func (d Decimal) StringFixedBank(places int32) string {
rounded := d.RoundBank(places)
return rounded.string(false)
@@ -1073,9 +1518,8 @@ func (d Decimal) StringFixedCash(interval uint8) string {
//
// Example:
//
-// NewFromFloat(5.45).Round(1).String() // output: "5.5"
-// NewFromFloat(545).Round(-1).String() // output: "550"
-//
+// 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
@@ -1104,11 +1548,10 @@ func (d Decimal) Round(places int32) Decimal {
//
// Example:
//
-// NewFromFloat(545).RoundCeil(-2).String() // output: "600"
-// NewFromFloat(500).RoundCeil(-2).String() // output: "500"
-// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11"
-// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.5"
-//
+// NewFromFloat(545).RoundCeil(-2).String() // output: "600"
+// NewFromFloat(500).RoundCeil(-2).String() // output: "500"
+// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11"
+// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.4"
func (d Decimal) RoundCeil(places int32) Decimal {
if d.exp >= -places {
return d
@@ -1130,11 +1573,10 @@ func (d Decimal) RoundCeil(places int32) Decimal {
//
// Example:
//
-// NewFromFloat(545).RoundFloor(-2).String() // output: "500"
-// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500"
-// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1"
-// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.4"
-//
+// NewFromFloat(545).RoundFloor(-2).String() // output: "500"
+// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500"
+// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1"
+// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.5"
func (d Decimal) RoundFloor(places int32) Decimal {
if d.exp >= -places {
return d
@@ -1156,11 +1598,10 @@ func (d Decimal) RoundFloor(places int32) Decimal {
//
// Example:
//
-// NewFromFloat(545).RoundUp(-2).String() // output: "600"
-// NewFromFloat(500).RoundUp(-2).String() // output: "500"
-// NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11"
-// NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.4"
-//
+// NewFromFloat(545).RoundUp(-2).String() // output: "600"
+// NewFromFloat(500).RoundUp(-2).String() // output: "500"
+// NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11"
+// NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.5"
func (d Decimal) RoundUp(places int32) Decimal {
if d.exp >= -places {
return d
@@ -1184,11 +1625,10 @@ func (d Decimal) RoundUp(places int32) Decimal {
//
// Example:
//
-// NewFromFloat(545).RoundDown(-2).String() // output: "500"
-// NewFromFloat(-500).RoundDown(-2).String() // output: "-500"
-// NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1"
-// NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.5"
-//
+// NewFromFloat(545).RoundDown(-2).String() // output: "500"
+// NewFromFloat(-500).RoundDown(-2).String() // output: "-500"
+// NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1"
+// NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.4"
func (d Decimal) RoundDown(places int32) Decimal {
if d.exp >= -places {
return d
@@ -1209,13 +1649,12 @@ func (d Decimal) RoundDown(places int32) Decimal {
//
// Examples:
//
-// NewFromFloat(5.45).RoundBank(1).String() // output: "5.4"
-// NewFromFloat(545).RoundBank(-1).String() // output: "540"
-// NewFromFloat(5.46).RoundBank(1).String() // output: "5.5"
-// NewFromFloat(546).RoundBank(-1).String() // output: "550"
-// NewFromFloat(5.55).RoundBank(1).String() // output: "5.6"
-// NewFromFloat(555).RoundBank(-1).String() // output: "560"
-//
+// NewFromFloat(5.45).RoundBank(1).String() // output: "5.4"
+// NewFromFloat(545).RoundBank(-1).String() // output: "540"
+// NewFromFloat(5.46).RoundBank(1).String() // output: "5.5"
+// NewFromFloat(546).RoundBank(-1).String() // output: "550"
+// NewFromFloat(5.55).RoundBank(1).String() // output: "5.6"
+// NewFromFloat(555).RoundBank(-1).String() // output: "560"
func (d Decimal) RoundBank(places int32) Decimal {
round := d.Round(places)
@@ -1237,11 +1676,13 @@ func (d Decimal) RoundBank(places int32) Decimal {
// interval. The amount payable for a cash transaction is rounded to the nearest
// multiple of the minimum currency unit available. The following intervals are
// available: 5, 10, 25, 50 and 100; any other number throws a panic.
-// 5: 5 cent rounding 3.43 => 3.45
-// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up)
-// 25: 25 cent rounding 3.41 => 3.50
-// 50: 50 cent rounding 3.75 => 4.00
-// 100: 100 cent rounding 3.50 => 4.00
+//
+// 5: 5 cent rounding 3.43 => 3.45
+// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up)
+// 25: 25 cent rounding 3.41 => 3.50
+// 50: 50 cent rounding 3.75 => 4.00
+// 100: 100 cent rounding 3.50 => 4.00
+//
// For more details: https://en.wikipedia.org/wiki/Cash_rounding
func (d Decimal) RoundCash(interval uint8) Decimal {
var iVal *big.Int
@@ -1310,8 +1751,7 @@ func (d Decimal) Ceil() Decimal {
//
// Example:
//
-// decimal.NewFromString("123.456").Truncate(2).String() // "123.45"
-//
+// decimal.NewFromString("123.456").Truncate(2).String() // "123.45"
func (d Decimal) Truncate(precision int32) Decimal {
d.ensureInitialized()
if precision >= 0 && -precision > d.exp {
@@ -1373,19 +1813,18 @@ func (d *Decimal) UnmarshalBinary(data []byte) error {
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (d Decimal) MarshalBinary() (data []byte, err error) {
- // Write the exponent first since it's a fixed size
- v1 := make([]byte, 4)
- binary.BigEndian.PutUint32(v1, uint32(d.exp))
-
- // Add the value
- var v2 []byte
- if v2, err = d.value.GobEncode(); err != nil {
- return
+ // exp is written first, but encode value first to know output size
+ var valueData []byte
+ if valueData, err = d.value.GobEncode(); err != nil {
+ return nil, err
}
+ // Write the exponent in front, since it's a fixed size
+ expData := make([]byte, 4, len(valueData)+4)
+ binary.BigEndian.PutUint32(expData, uint32(d.exp))
+
// Return the byte array
- data = append(v1, v2...)
- return
+ return append(expData, valueData...), nil
}
// Scan implements the sql.Scanner interface for database deserialization.
@@ -1408,6 +1847,11 @@ func (d *Decimal) Scan(value interface{}) error {
*d = New(v, 0)
return nil
+ case uint64:
+ // while clickhouse may send 0 in db as uint64
+ *d = NewFromUint64(v)
+ return nil
+
default:
// default is trying to interpret value stored as string
str, err := unquoteIfQuoted(v)
@@ -1455,7 +1899,8 @@ func (d *Decimal) GobDecode(data []byte) error {
}
// StringScaled first scales the decimal then calls .String() on it.
-// NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead.
+//
+// Deprecated: buggy and unintuitive. Use StringFixed instead.
func (d Decimal) StringScaled(exp int32) string {
return d.rescale(exp).String()
}
@@ -1515,7 +1960,7 @@ func (d *Decimal) ensureInitialized() {
//
// To call this function with an array, you must do:
//
-// Min(arr[0], arr[1:]...)
+// Min(arr[0], arr[1:]...)
//
// This makes it harder to accidentally call Min with 0 arguments.
func Min(first Decimal, rest ...Decimal) Decimal {
@@ -1532,7 +1977,7 @@ func Min(first Decimal, rest ...Decimal) Decimal {
//
// To call this function with an array, you must do:
//
-// Max(arr[0], arr[1:]...)
+// Max(arr[0], arr[1:]...)
//
// This makes it harder to accidentally call Max with 0 arguments.
func Max(first Decimal, rest ...Decimal) Decimal {
@@ -1567,22 +2012,13 @@ func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) {
d1.ensureInitialized()
d2.ensureInitialized()
- if d1.exp == d2.exp {
- return d1, d2
+ if d1.exp < d2.exp {
+ return d1, d2.rescale(d1.exp)
+ } else if d1.exp > d2.exp {
+ return d1.rescale(d2.exp), 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
+ return d1, d2
}
func unquoteIfQuoted(value interface{}) (string, error) {
@@ -1594,8 +2030,7 @@ func unquoteIfQuoted(value interface{}) (string, error) {
case []byte:
bytes = v
default:
- return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'",
- value, value)
+ return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'", value, value)
}
// If the amount is quoted, strip the quotes