mirror of
https://github.com/shopspring/decimal.git
synced 2024-11-26 22:15:08 +01:00
Compare commits
13 commits
e117758075
...
08c7c48074
Author | SHA1 | Date | |
---|---|---|---|
|
08c7c48074 | ||
|
a1bdfc355e | ||
|
275e48eaef | ||
|
dd603cbbbd | ||
|
80ec940054 | ||
|
2fa107d0fe | ||
|
645a76e5b0 | ||
|
ed8f76e893 | ||
|
0e69d5cd53 | ||
|
bf7794e1a8 | ||
|
547861c49b | ||
|
78289cc844 | ||
|
2b68c56fe0 |
7 changed files with 751 additions and 143 deletions
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
@ -1,11 +1,15 @@
|
||||||
name: ci
|
name: ci
|
||||||
on: [push]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
jobs:
|
jobs:
|
||||||
ci-job:
|
ci-job:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go: [ '1.7.x', '1.18', '1.19', '1.20', '1.21', '1.x' ]
|
go: [ '1.10.x', '1.19', '1.20', '1.21', '1.22', '1.x' ]
|
||||||
name: Go ${{ matrix.go }}
|
name: Go ${{ matrix.go }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
27
CHANGELOG.md
27
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
|
## Decimal v1.3.1
|
||||||
|
|
||||||
#### ENHANCEMENTS
|
#### ENHANCEMENTS
|
||||||
|
|
21
README.md
21
README.md
|
@ -22,7 +22,12 @@ Run `go get github.com/shopspring/decimal`
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Decimal library requires Go version `>=1.7`
|
Decimal library requires Go version `>=1.10`
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
http://godoc.org/github.com/shopspring/decimal
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -59,14 +64,16 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Alternative libraries
|
||||||
|
|
||||||
http://godoc.org/github.com/shopspring/decimal
|
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 :)
|
||||||
|
|
||||||
## Production Usage
|
* [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
|
||||||
* [Spring](https://shopspring.com/), since August 14, 2014.
|
* [govalues/decimal](https://github.com/govalues/decimal) - high performance, zero-allocation, low precision (19 digits)
|
||||||
* If you are using this in production, please let us know!
|
* [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
|
## FAQ
|
||||||
|
|
||||||
|
|
381
decimal.go
381
decimal.go
|
@ -43,6 +43,20 @@ import (
|
||||||
// d4.String() // output: "0.667"
|
// d4.String() // output: "0.667"
|
||||||
var DivisionPrecision = 16
|
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
|
// MarshalJSONWithoutQuotes should be set to true if you want the decimal to
|
||||||
// be JSON marshaled as a number, instead of as a string.
|
// be JSON marshaled as a number, instead of as a string.
|
||||||
// WARNING: this is dangerous for decimals with many digits, since many JSON
|
// WARNING: this is dangerous for decimals with many digits, since many JSON
|
||||||
|
@ -116,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
|
// NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp
|
||||||
func NewFromBigInt(value *big.Int, exp int32) Decimal {
|
func NewFromBigInt(value *big.Int, exp int32) Decimal {
|
||||||
return Decimal{
|
return Decimal{
|
||||||
|
@ -129,11 +155,10 @@ func NewFromBigInt(value *big.Int, exp int32) Decimal {
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// d1 := NewFromBigRat(big.NewRat(0, 1), 0) // output: "0"
|
// d1 := NewFromBigRat(big.NewRat(0, 1), 0) // output: "0"
|
||||||
// d2 := NewFromBigRat(big.NewRat(4, 5), 1) // output: "0.8"
|
// d2 := NewFromBigRat(big.NewRat(4, 5), 1) // output: "0.8"
|
||||||
// d3 := NewFromBigRat(big.NewRat(1000, 3), 3) // output: "333.333"
|
// d3 := NewFromBigRat(big.NewRat(1000, 3), 3) // output: "333.333"
|
||||||
// d4 := NewFromBigRat(big.NewRat(2, 7), 4) // output: "0.2857"
|
// d4 := NewFromBigRat(big.NewRat(2, 7), 4) // output: "0.2857"
|
||||||
//
|
|
||||||
func NewFromBigRat(value *big.Rat, precision int32) Decimal {
|
func NewFromBigRat(value *big.Rat, precision int32) Decimal {
|
||||||
return Decimal{
|
return Decimal{
|
||||||
value: new(big.Int).Set(value.Num()),
|
value: new(big.Int).Set(value.Num()),
|
||||||
|
@ -650,20 +675,274 @@ func (d Decimal) Mod(d2 Decimal) Decimal {
|
||||||
return r
|
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 {
|
func (d Decimal) Pow(d2 Decimal) Decimal {
|
||||||
var temp Decimal
|
baseSign := d.Sign()
|
||||||
if d2.IntPart() == 0 {
|
expSign := d2.Sign()
|
||||||
return NewFromFloat(1)
|
|
||||||
|
if baseSign == 0 {
|
||||||
|
if expSign == 0 {
|
||||||
|
return Decimal{}
|
||||||
|
}
|
||||||
|
if expSign == 1 {
|
||||||
|
return Decimal{zeroInt, 0}
|
||||||
|
}
|
||||||
|
if expSign == -1 {
|
||||||
|
return Decimal{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
temp = d.Pow(d2.Div(NewFromFloat(2)))
|
|
||||||
if d2.IntPart()%2 == 0 {
|
if expSign == 0 {
|
||||||
return temp.Mul(temp)
|
return Decimal{oneInt, 0}
|
||||||
}
|
}
|
||||||
if d2.IntPart() > 0 {
|
|
||||||
return temp.Mul(temp).Mul(d)
|
// TODO: optimize extraction of fractional part
|
||||||
|
one := Decimal{oneInt, 0}
|
||||||
|
expIntPart, expFracPart := d2.QuoRem(one, 0)
|
||||||
|
|
||||||
|
if baseSign == -1 && !expFracPart.IsZero() {
|
||||||
|
return Decimal{}
|
||||||
}
|
}
|
||||||
return temp.Mul(temp).Div(d)
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 isExpNeg {
|
||||||
|
return New(1, 0).DivRound(result, precision), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm.
|
// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm.
|
||||||
|
@ -920,7 +1199,10 @@ func (d Decimal) Ln(precision int32) (Decimal, error) {
|
||||||
// Halley's Iteration.
|
// Halley's Iteration.
|
||||||
// Calculating n-th term of formula: a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z),
|
// 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
|
// until the difference between current and next term is smaller than epsilon
|
||||||
for {
|
var prevStep Decimal
|
||||||
|
maxIters := calcPrecision*2 + 10
|
||||||
|
|
||||||
|
for i := int32(0); i < maxIters; i++ {
|
||||||
// exp(a_n)
|
// exp(a_n)
|
||||||
comp3, _ = comp1.ExpTaylor(calcPrecision)
|
comp3, _ = comp1.ExpTaylor(calcPrecision)
|
||||||
// exp(a_n) - z
|
// exp(a_n) - z
|
||||||
|
@ -934,9 +1216,17 @@ func (d Decimal) Ln(precision int32) (Decimal, error) {
|
||||||
// comp1 = a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z)
|
// comp1 = a_(n+1) = a_n - 2 * (exp(a_n) - z) / (exp(a_n) + z)
|
||||||
comp1 = comp1.Sub(comp3)
|
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 {
|
if comp3.Abs().Cmp(epsilon) <= 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevStep = comp3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -946,14 +1236,33 @@ func (d Decimal) Ln(precision int32) (Decimal, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NumDigits returns the number of digits of the decimal coefficient (d.Value)
|
// 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 {
|
func (d Decimal) NumDigits() int {
|
||||||
d.ensureInitialized()
|
if d.value == nil {
|
||||||
// Note(mwoss): It can be optimized, unnecessary cast of big.Int to string
|
return 1
|
||||||
if d.IsNegative() {
|
|
||||||
return len(d.value.String()) - 1
|
|
||||||
}
|
}
|
||||||
return len(d.value.String())
|
|
||||||
|
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 estimatedNumDigits
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
|
// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false.
|
||||||
|
@ -1109,9 +1418,7 @@ func (d Decimal) IntPart() int64 {
|
||||||
// BigInt returns integer component of the decimal as a BigInt.
|
// BigInt returns integer component of the decimal as a BigInt.
|
||||||
func (d Decimal) BigInt() *big.Int {
|
func (d Decimal) BigInt() *big.Int {
|
||||||
scaledD := d.rescale(0)
|
scaledD := d.rescale(0)
|
||||||
i := &big.Int{}
|
return scaledD.value
|
||||||
i.SetString(scaledD.String(), 10)
|
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BigFloat returns decimal as BigFloat.
|
// BigFloat returns decimal as BigFloat.
|
||||||
|
@ -1506,19 +1813,18 @@ func (d *Decimal) UnmarshalBinary(data []byte) error {
|
||||||
|
|
||||||
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||||
func (d Decimal) MarshalBinary() (data []byte, err error) {
|
func (d Decimal) MarshalBinary() (data []byte, err error) {
|
||||||
// Write the exponent first since it's a fixed size
|
// exp is written first, but encode value first to know output size
|
||||||
v1 := make([]byte, 4)
|
var valueData []byte
|
||||||
binary.BigEndian.PutUint32(v1, uint32(d.exp))
|
if valueData, err = d.value.GobEncode(); err != nil {
|
||||||
|
return nil, err
|
||||||
// Add the value
|
|
||||||
var v2 []byte
|
|
||||||
if v2, err = d.value.GobEncode(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Return the byte array
|
||||||
data = append(v1, v2...)
|
return append(expData, valueData...), nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface for database deserialization.
|
// Scan implements the sql.Scanner interface for database deserialization.
|
||||||
|
@ -1561,6 +1867,11 @@ func (d *Decimal) Scan(value interface{}) error {
|
||||||
*d = New(v, 0)
|
*d = New(v, 0)
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
// while clickhouse may send 0 in db as uint64
|
||||||
|
*d = NewFromUint64(v)
|
||||||
|
return nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// default is trying to interpret value stored as string
|
// default is trying to interpret value stored as string
|
||||||
str, err := unquoteIfQuoted(v)
|
str, err := unquoteIfQuoted(v)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package decimal
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -120,6 +121,34 @@ func BenchmarkDecimal_RoundCash_Five(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func numDigits(b *testing.B, want int, val Decimal) {
|
||||||
|
b.Helper()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if have := val.NumDigits(); have != want {
|
||||||
|
b.Fatalf("\nHave: %d\nWant: %d", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_NumDigits10(b *testing.B) {
|
||||||
|
numDigits(b, 10, New(3478512345, -3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_NumDigits100(b *testing.B) {
|
||||||
|
s := make([]byte, 102)
|
||||||
|
for i := range s {
|
||||||
|
s[i] = byte('0' + i%10)
|
||||||
|
}
|
||||||
|
s[0] = '-'
|
||||||
|
s[100] = '.'
|
||||||
|
d, err := NewFromString(string(s))
|
||||||
|
if err != nil {
|
||||||
|
b.Log(d)
|
||||||
|
b.Error(err)
|
||||||
|
}
|
||||||
|
numDigits(b, 100, d)
|
||||||
|
}
|
||||||
|
|
||||||
func Benchmark_Cmp(b *testing.B) {
|
func Benchmark_Cmp(b *testing.B) {
|
||||||
decimals := DecimalSlice([]Decimal{})
|
decimals := DecimalSlice([]Decimal{})
|
||||||
for i := 0; i < 1000000; i++ {
|
for i := 0; i < 1000000; i++ {
|
||||||
|
@ -131,7 +160,7 @@ func Benchmark_Cmp(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_decimal_Decimal_Add_different_precision(b *testing.B) {
|
func BenchmarkDecimal_Add_different_precision(b *testing.B) {
|
||||||
d1 := NewFromFloat(1000.123)
|
d1 := NewFromFloat(1000.123)
|
||||||
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
||||||
|
|
||||||
|
@ -142,7 +171,7 @@ func Benchmark_decimal_Decimal_Add_different_precision(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_decimal_Decimal_Sub_different_precision(b *testing.B) {
|
func BenchmarkDecimal_Sub_different_precision(b *testing.B) {
|
||||||
d1 := NewFromFloat(1000.123)
|
d1 := NewFromFloat(1000.123)
|
||||||
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
d2 := NewFromFloat(500).Mul(NewFromFloat(0.12))
|
||||||
|
|
||||||
|
@ -153,7 +182,7 @@ func Benchmark_decimal_Decimal_Sub_different_precision(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_decimal_Decimal_Add_same_precision(b *testing.B) {
|
func BenchmarkDecimal_Add_same_precision(b *testing.B) {
|
||||||
d1 := NewFromFloat(1000.123)
|
d1 := NewFromFloat(1000.123)
|
||||||
d2 := NewFromFloat(500.123)
|
d2 := NewFromFloat(500.123)
|
||||||
|
|
||||||
|
@ -164,7 +193,7 @@ func Benchmark_decimal_Decimal_Add_same_precision(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_decimal_Decimal_Sub_same_precision(b *testing.B) {
|
func BenchmarkDecimal_Sub_same_precision(b *testing.B) {
|
||||||
d1 := NewFromFloat(1000.123)
|
d1 := NewFromFloat(1000.123)
|
||||||
d2 := NewFromFloat(500.123)
|
d2 := NewFromFloat(500.123)
|
||||||
|
|
||||||
|
@ -185,6 +214,41 @@ func BenchmarkDecimal_IsInteger(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_Pow(b *testing.B) {
|
||||||
|
d1 := RequireFromString("5.2")
|
||||||
|
d2 := RequireFromString("6.3")
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
d1.Pow(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_PowWithPrecision(b *testing.B) {
|
||||||
|
d1 := RequireFromString("5.2")
|
||||||
|
d2 := RequireFromString("6.3")
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = d1.PowWithPrecision(d2, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func BenchmarkDecimal_PowInt32(b *testing.B) {
|
||||||
|
d1 := RequireFromString("5.2")
|
||||||
|
d2 := int32(10)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = d1.PowInt32(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecimal_PowBigInt(b *testing.B) {
|
||||||
|
d1 := RequireFromString("5.2")
|
||||||
|
d2 := big.NewInt(10)
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, _ = d1.PowBigInt(d2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkDecimal_NewFromString(b *testing.B) {
|
func BenchmarkDecimal_NewFromString(b *testing.B) {
|
||||||
count := 72
|
count := 72
|
||||||
prices := make([]string, 0, count)
|
prices := make([]string, 0, count)
|
||||||
|
|
383
decimal_test.go
383
decimal_test.go
|
@ -476,10 +476,11 @@ func TestNewFromFloatWithExponent(t *testing.T) {
|
||||||
|
|
||||||
func TestNewFromInt(t *testing.T) {
|
func TestNewFromInt(t *testing.T) {
|
||||||
tests := map[int64]string{
|
tests := map[int64]string{
|
||||||
0: "0",
|
0: "0",
|
||||||
1: "1",
|
1: "1",
|
||||||
323412345: "323412345",
|
323412345: "323412345",
|
||||||
9223372036854775807: "9223372036854775807",
|
9223372036854775807: "9223372036854775807",
|
||||||
|
-9223372036854775808: "-9223372036854775808",
|
||||||
}
|
}
|
||||||
|
|
||||||
// add negatives
|
// add negatives
|
||||||
|
@ -501,10 +502,11 @@ func TestNewFromInt(t *testing.T) {
|
||||||
|
|
||||||
func TestNewFromInt32(t *testing.T) {
|
func TestNewFromInt32(t *testing.T) {
|
||||||
tests := map[int32]string{
|
tests := map[int32]string{
|
||||||
0: "0",
|
0: "0",
|
||||||
1: "1",
|
1: "1",
|
||||||
323412345: "323412345",
|
323412345: "323412345",
|
||||||
2147483647: "2147483647",
|
2147483647: "2147483647",
|
||||||
|
-2147483648: "-2147483648",
|
||||||
}
|
}
|
||||||
|
|
||||||
// add negatives
|
// add negatives
|
||||||
|
@ -524,6 +526,25 @@ func TestNewFromInt32(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewFromUint64(t *testing.T) {
|
||||||
|
tests := map[uint64]string{
|
||||||
|
0: "0",
|
||||||
|
1: "1",
|
||||||
|
323412345: "323412345",
|
||||||
|
9223372036854775807: "9223372036854775807",
|
||||||
|
18446744073709551615: "18446744073709551615",
|
||||||
|
}
|
||||||
|
|
||||||
|
for input, s := range tests {
|
||||||
|
d := NewFromUint64(input)
|
||||||
|
if d.String() != s {
|
||||||
|
t.Errorf("expected %s, got %s (%s, %d)",
|
||||||
|
s, d.String(),
|
||||||
|
d.value.String(), d.exp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewFromBigIntWithExponent(t *testing.T) {
|
func TestNewFromBigIntWithExponent(t *testing.T) {
|
||||||
type Inp struct {
|
type Inp struct {
|
||||||
val *big.Int
|
val *big.Int
|
||||||
|
@ -2395,104 +2416,57 @@ func TestDecimal_Max(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecimal_Scan(t *testing.T) {
|
func scanHelper(t *testing.T, dbval interface{}, expected Decimal) {
|
||||||
// test the Scan method that implements the
|
t.Helper()
|
||||||
// sql.Scanner interface
|
|
||||||
// check for the for different type of values
|
|
||||||
// that are possible to be received from the database
|
|
||||||
// drivers
|
|
||||||
|
|
||||||
// in normal operations the db driver (sqlite at least)
|
|
||||||
// will return an int64 if you specified a numeric format
|
|
||||||
a := Decimal{}
|
a := Decimal{}
|
||||||
|
if err := a.Scan(dbval); err != nil {
|
||||||
|
// Scan failed... no need to test result value
|
||||||
|
t.Errorf("a.Scan(%v) failed with message: %s", dbval, err)
|
||||||
|
} else if !a.Equal(expected) {
|
||||||
|
// Scan succeeded... test resulting values
|
||||||
|
t.Errorf("%s does not equal to %s", a, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_Scan(t *testing.T) {
|
||||||
|
// test the Scan method that implements the sql.Scanner interface
|
||||||
|
// check different types received from various database drivers
|
||||||
|
|
||||||
dbvalue := 54.33
|
dbvalue := 54.33
|
||||||
expected := NewFromFloat(dbvalue)
|
expected := NewFromFloat(dbvalue)
|
||||||
|
scanHelper(t, dbvalue, expected)
|
||||||
err := a.Scan(dbvalue)
|
|
||||||
if err != nil {
|
|
||||||
// Scan failed... no need to test result value
|
|
||||||
t.Errorf("a.Scan(54.33) failed with message: %s", err)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Scan succeeded... test resulting values
|
|
||||||
if !a.Equal(expected) {
|
|
||||||
t.Errorf("%s does not equal to %s", a, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// apparently MySQL 5.7.16 and returns these as float32 so we need
|
// apparently MySQL 5.7.16 and returns these as float32 so we need
|
||||||
// to handle these as well
|
// to handle these as well
|
||||||
dbvalueFloat32 := float32(54.33)
|
dbvalueFloat32 := float32(54.33)
|
||||||
expected = NewFromFloat(float64(dbvalueFloat32))
|
expected = NewFromFloat(float64(dbvalueFloat32))
|
||||||
|
scanHelper(t, dbvalueFloat32, expected)
|
||||||
err = a.Scan(dbvalueFloat32)
|
|
||||||
if err != nil {
|
|
||||||
// Scan failed... no need to test result value
|
|
||||||
t.Errorf("a.Scan(54.33) failed with message: %s", err)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Scan succeeded... test resulting values
|
|
||||||
if !a.Equal(expected) {
|
|
||||||
t.Errorf("%s does not equal to %s", a, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// at least SQLite returns an int64 when 0 is stored in the db
|
// at least SQLite returns an int64 when 0 is stored in the db
|
||||||
// and you specified a numeric format on the schema
|
// and you specified a numeric format on the schema
|
||||||
dbvalueInt := int64(0)
|
dbvalueInt := int64(0)
|
||||||
expected = New(dbvalueInt, 0)
|
expected = New(dbvalueInt, 0)
|
||||||
|
scanHelper(t, dbvalueInt, expected)
|
||||||
|
|
||||||
err = a.Scan(dbvalueInt)
|
// also test uint64
|
||||||
if err != nil {
|
dbvalueUint64 := uint64(2)
|
||||||
// Scan failed... no need to test result value
|
expected = New(2, 0)
|
||||||
t.Errorf("a.Scan(0) failed with message: %s", err)
|
scanHelper(t, dbvalueUint64, expected)
|
||||||
|
|
||||||
} else {
|
|
||||||
// Scan succeeded... test resulting values
|
|
||||||
if !a.Equal(expected) {
|
|
||||||
t.Errorf("%s does not equal to %s", a, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// in case you specified a varchar in your SQL schema,
|
// in case you specified a varchar in your SQL schema,
|
||||||
// the database driver will return byte slice []byte
|
// the database driver may return either []byte or string
|
||||||
valueStr := "535.666"
|
valueStr := "535.666"
|
||||||
dbvalueStr := []byte(valueStr)
|
dbvalueStr := []byte(valueStr)
|
||||||
expected, err = NewFromString(valueStr)
|
expected, err := NewFromString(valueStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
scanHelper(t, dbvalueStr, expected)
|
||||||
err = a.Scan(dbvalueStr)
|
scanHelper(t, valueStr, expected)
|
||||||
if err != nil {
|
|
||||||
// Scan failed... no need to test result value
|
|
||||||
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Scan succeeded... test resulting values
|
|
||||||
if !a.Equal(expected) {
|
|
||||||
t.Errorf("%s does not equal to %s", a, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lib/pq can also return strings
|
|
||||||
expected, err = NewFromString(valueStr)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.Scan(valueStr)
|
|
||||||
if err != nil {
|
|
||||||
// Scan failed... no need to test result value
|
|
||||||
t.Errorf("a.Scan('535.666') failed with message: %s", err)
|
|
||||||
} else {
|
|
||||||
// Scan succeeded... test resulting values
|
|
||||||
if !a.Equal(expected) {
|
|
||||||
t.Errorf("%s does not equal to %s", a, expected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type foo struct{}
|
type foo struct{}
|
||||||
|
a := Decimal{}
|
||||||
err = a.Scan(foo{})
|
err = a.Scan(foo{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("a.Scan(Foo{}) should have thrown an error but did not")
|
t.Errorf("a.Scan(Foo{}) should have thrown an error but did not")
|
||||||
|
@ -2681,21 +2655,241 @@ func TestDecimal_Cmp2(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPow(t *testing.T) {
|
func TestDecimal_Pow(t *testing.T) {
|
||||||
a := New(4, 0)
|
for _, testCase := range []struct {
|
||||||
b := New(2, 0)
|
Base string
|
||||||
x := a.Pow(b)
|
Exponent string
|
||||||
if x.String() != "16" {
|
Expected string
|
||||||
t.Errorf("Error, saw %s", x.String())
|
}{
|
||||||
|
{"0.0", "1.0", "0.0"},
|
||||||
|
{"0.0", "5.7", "0.0"},
|
||||||
|
{"0.0", "-3.2", "0.0"},
|
||||||
|
{"3.13", "0.0", "1.0"},
|
||||||
|
{"-591.5", "0.0", "1.0"},
|
||||||
|
{"3.0", "3.0", "27.0"},
|
||||||
|
{"3.0", "10.0", "59049.0"},
|
||||||
|
{"3.13", "5.0", "300.4150512793"},
|
||||||
|
{"4.0", "2.0", "16.0"},
|
||||||
|
{"4.0", "-2.0", "0.0625"},
|
||||||
|
{"629.25", "5.0", "98654323103449.5673828125"},
|
||||||
|
{"5.0", "5.73", "10118.08037159375"},
|
||||||
|
{"962.0", "3.2791", "6055212360.0000044205714144"},
|
||||||
|
{"5.69169126", "5.18515912", "8242.26344757948412597909547972726268869189399260047793106028930864"},
|
||||||
|
{"13.1337", "3.5196719618391835", "8636.856220644773844815693636723928750940666269885"},
|
||||||
|
{"67762386.283696923", "4.85917691669163916681738", "112761146905370140621385730157437443321.91755738117317148674362233906499698561022574811238435007575701773212242750262081945556470501"},
|
||||||
|
{"-3.0", "6.0", "729"},
|
||||||
|
{"-13.757", "5.0", "-492740.983929899460557"},
|
||||||
|
{"3.0", "-6.0", "0.0013717421124829"},
|
||||||
|
{"13.757", "-5.0", "0.000002029463821"},
|
||||||
|
{"66.12", "-7.61313", "0.000000000000013854086588876805036"},
|
||||||
|
{"6696871.12", "-2.61313", "0.000000000000000001455988684546983"},
|
||||||
|
{"-3.0", "-6.0", "0.0013717421124829"},
|
||||||
|
{"-13.757", "-5.0", "-0.000002029463821"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Base)
|
||||||
|
exp, _ := NewFromString(testCase.Exponent)
|
||||||
|
expected, _ := NewFromString(testCase.Expected)
|
||||||
|
|
||||||
|
result := base.Pow(exp)
|
||||||
|
|
||||||
|
if result.Cmp(expected) != 0 {
|
||||||
|
t.Errorf("expected %s, got %s, for %s^%s", testCase.Expected, result.String(), testCase.Base, testCase.Exponent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNegativePow(t *testing.T) {
|
func TestDecimal_PowWithPrecision(t *testing.T) {
|
||||||
a := New(4, 0)
|
for _, testCase := range []struct {
|
||||||
b := New(-2, 0)
|
Base string
|
||||||
x := a.Pow(b)
|
Exponent string
|
||||||
if x.String() != "0.0625" {
|
Precision int32
|
||||||
t.Errorf("Error, saw %s", x.String())
|
Expected string
|
||||||
|
}{
|
||||||
|
{"0.0", "1.0", 2, "0.0"},
|
||||||
|
{"0.0", "5.7", 2, "0.0"},
|
||||||
|
{"0.0", "-3.2", 2, "0.0"},
|
||||||
|
{"3.13", "0.0", 2, "1.0"},
|
||||||
|
{"-591.5", "0.0", 2, "1.0"},
|
||||||
|
{"3.0", "3.0", 2, "27.0"},
|
||||||
|
{"3.0", "10.0", 2, "59049.0"},
|
||||||
|
{"3.13", "5.0", 5, "300.4150512793"},
|
||||||
|
{"4.0", "2.0", 2, "16.0"},
|
||||||
|
{"4.0", "-2.0", 2, "0.06"},
|
||||||
|
{"4.0", "-2.0", 4, "0.0625"},
|
||||||
|
{"629.25", "5.0", 6, "98654323103449.5673828125"},
|
||||||
|
{"5.0", "5.73", 20, "10118.080371595019317118681359884375"},
|
||||||
|
{"962.0", "3.2791", 15, "6055212360.000004406551603058195732"},
|
||||||
|
{"5.69169126", "5.18515912", 4, "8242.26344757948412587366859330429895955552280978668983459852256"},
|
||||||
|
{"13.1337", "3.5196719618391835", 8, "8636.85622064477384481569363672392591908386390769375"},
|
||||||
|
{"67762386.283696923", "4.85917691669163916681738", 10, "112761146905370140621385730157437443321.917557381173174638304347353880676293576708009282115993465286373470882947470198597518762"},
|
||||||
|
{"-3.0", "6.0", 2, "729"},
|
||||||
|
{"-13.757", "5.0", 4, "-492740.983929899460557"},
|
||||||
|
{"3.0", "-6.0", 10, "0.0013717421"},
|
||||||
|
{"13.757", "-5.0", 20, "0.00000202946382098037"},
|
||||||
|
{"66.12", "-7.61313", 20, "0.00000000000001385381563049821591633907104023700216"},
|
||||||
|
{"6696871.12", "-2.61313", 24, "0.0000000000000000014558252733872790626400278983397459207418"},
|
||||||
|
{"-3.0", "-6.0", 8, "0.00137174"},
|
||||||
|
{"-13.757", "-5.0", 16, "-0.000002029463821"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Base)
|
||||||
|
exp, _ := NewFromString(testCase.Exponent)
|
||||||
|
expected, _ := NewFromString(testCase.Expected)
|
||||||
|
|
||||||
|
result, _ := base.PowWithPrecision(exp, testCase.Precision)
|
||||||
|
|
||||||
|
if result.Cmp(expected) != 0 {
|
||||||
|
t.Errorf("expected %s, got %s, for %s^%s", testCase.Expected, result.String(), testCase.Base, testCase.Exponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowWithPrecision_Infinity(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
Base string
|
||||||
|
Exponent string
|
||||||
|
}{
|
||||||
|
{"0.0", "0.0"},
|
||||||
|
{"0.0", "-2.0"},
|
||||||
|
{"0.0", "-4.6"},
|
||||||
|
{"-66.12", "7.61313"}, // Imaginary value
|
||||||
|
{"-5696871.12", "5.61313"}, // Imaginary value
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Base)
|
||||||
|
exp, _ := NewFromString(testCase.Exponent)
|
||||||
|
|
||||||
|
_, err := base.PowWithPrecision(exp, 5)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("lool it should be error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowWithPrecision_UndefinedResult(t *testing.T) {
|
||||||
|
base := RequireFromString("0")
|
||||||
|
exponent := RequireFromString("0")
|
||||||
|
|
||||||
|
_, err := base.PowWithPrecision(exponent, 4)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, cannot be represent undefined value of 0**0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowWithPrecision_InfinityResult(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
Base string
|
||||||
|
Exponent string
|
||||||
|
}{
|
||||||
|
{"0.0", "-2.0"},
|
||||||
|
{"0.0", "-4.6"},
|
||||||
|
{"0.0", "-9239.671333"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Base)
|
||||||
|
exp, _ := NewFromString(testCase.Exponent)
|
||||||
|
|
||||||
|
_, err := base.PowWithPrecision(exp, 4)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, cannot represent infinity value of 0 ** y, where y < 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowWithPrecision_ImaginaryResult(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
Base string
|
||||||
|
Exponent string
|
||||||
|
}{
|
||||||
|
{"-0.2261", "106.12"},
|
||||||
|
{"-66.12", "7.61313"},
|
||||||
|
{"-5696871.12", "5.61313"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Base)
|
||||||
|
exp, _ := NewFromString(testCase.Exponent)
|
||||||
|
|
||||||
|
_, err := base.PowWithPrecision(exp, 4)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, cannot represent imaginary value of x ** y, where x < 0 and y is non-integer decimal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowInt32(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
Decimal string
|
||||||
|
Exponent int32
|
||||||
|
Expected string
|
||||||
|
}{
|
||||||
|
{"0.0", 1, "0.0"},
|
||||||
|
{"3.13", 0, "1.0"},
|
||||||
|
{"-591.5", 0, "1.0"},
|
||||||
|
{"3.0", 3, "27.0"},
|
||||||
|
{"3.0", 10, "59049.0"},
|
||||||
|
{"3.13", 5, "300.4150512793"},
|
||||||
|
{"629.25", 5, "98654323103449.5673828125"},
|
||||||
|
{"-3.0", 6, "729"},
|
||||||
|
{"-13.757", 5, "-492740.983929899460557"},
|
||||||
|
{"3.0", -6, "0.0013717421124829"},
|
||||||
|
{"-13.757", -5, "-0.000002029463821"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Decimal)
|
||||||
|
expected, _ := NewFromString(testCase.Expected)
|
||||||
|
|
||||||
|
result, _ := base.PowInt32(testCase.Exponent)
|
||||||
|
|
||||||
|
if result.Cmp(expected) != 0 {
|
||||||
|
t.Errorf("expected %s, got %s, for %s**%d", testCase.Expected, result.String(), testCase.Decimal, testCase.Exponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowInt32_UndefinedResult(t *testing.T) {
|
||||||
|
base := RequireFromString("0")
|
||||||
|
|
||||||
|
_, err := base.PowInt32(0)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, cannot be represent undefined value of 0**0")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowBigInt(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
Decimal string
|
||||||
|
Exponent *big.Int
|
||||||
|
Expected string
|
||||||
|
}{
|
||||||
|
{"3.13", big.NewInt(0), "1.0"},
|
||||||
|
{"-591.5", big.NewInt(0), "1.0"},
|
||||||
|
{"3.0", big.NewInt(3), "27.0"},
|
||||||
|
{"3.0", big.NewInt(10), "59049.0"},
|
||||||
|
{"3.13", big.NewInt(5), "300.4150512793"},
|
||||||
|
{"629.25", big.NewInt(5), "98654323103449.5673828125"},
|
||||||
|
{"-3.0", big.NewInt(6), "729"},
|
||||||
|
{"-13.757", big.NewInt(5), "-492740.983929899460557"},
|
||||||
|
{"3.0", big.NewInt(-6), "0.0013717421124829"},
|
||||||
|
{"-13.757", big.NewInt(-5), "-0.000002029463821"},
|
||||||
|
} {
|
||||||
|
base, _ := NewFromString(testCase.Decimal)
|
||||||
|
expected, _ := NewFromString(testCase.Expected)
|
||||||
|
|
||||||
|
result, _ := base.PowBigInt(testCase.Exponent)
|
||||||
|
|
||||||
|
if result.Cmp(expected) != 0 {
|
||||||
|
t.Errorf("expected %s, got %s, for %s**%d", testCase.Expected, result.String(), testCase.Decimal, testCase.Exponent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimal_PowBigInt_UndefinedResult(t *testing.T) {
|
||||||
|
base := RequireFromString("0")
|
||||||
|
|
||||||
|
_, err := base.PowBigInt(big.NewInt(0))
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("expected error, undefined value of 0**0 cannot be represented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2882,6 +3076,7 @@ func TestDecimal_Ln(t *testing.T) {
|
||||||
{"839101.0351094726488848490572028502", 50, "13.64008640145229044389152437468283605382056561604272"},
|
{"839101.0351094726488848490572028502", 50, "13.64008640145229044389152437468283605382056561604272"},
|
||||||
{"5023583755703750094849.03519358513093500275017501750602739169823", 25, "49.9684305274348922267409953"},
|
{"5023583755703750094849.03519358513093500275017501750602739169823", 25, "49.9684305274348922267409953"},
|
||||||
{"5023583755703750094849.03519358513093500275017501750602739169823", -1, "50.0"},
|
{"5023583755703750094849.03519358513093500275017501750602739169823", -1, "50.0"},
|
||||||
|
{"66.12", 18, "4.191471272952823429"},
|
||||||
} {
|
} {
|
||||||
d, _ := NewFromString(testCase.Dec)
|
d, _ := NewFromString(testCase.Dec)
|
||||||
expected, _ := NewFromString(testCase.Expected)
|
expected, _ := NewFromString(testCase.Expected)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,3 +1,3 @@
|
||||||
module github.com/shopspring/decimal
|
module github.com/shopspring/decimal
|
||||||
|
|
||||||
go 1.7
|
go 1.10
|
||||||
|
|
Loading…
Reference in a new issue