Compare commits

...

20 commits

Author SHA1 Message Date
kempeng
ed771bb615
Merge aa8a681072 into d00399e161 2024-03-01 13:54:39 -08:00
Dovydas
d00399e161
Add NewFromBigRat constructor (#288) 2024-02-19 23:13:43 +01:00
Andrei Tudor Călin
57a340d853
Add Compare method which forwards calls to Cmp (#346)
Given the interface definition

	type Ordered[T any] interface {
		Compare(T) int
	}

And the type constraint T Ordered[T], make decimal.Decimal satisfy this
constraint, so that generic code written against T Ordered[T] can work
with decimal values as smoothly as it works with time.Time values today.

Fixes: #345
2024-01-24 23:48:15 +01:00
Chris Belsole
b79c571f80
Removed unnecessary min function for RescalePair method (#265)
Co-authored-by: Chris Belsole <chris.belsole@homelight.com>
2024-01-15 20:56:38 +01:00
Frank
b844c58a71
Fix overflow edge case in QuoRem method (#322) 2024-01-15 20:52:54 +01:00
Feras AlSaffar
142a0cf2f2
Use Godoc standard to mark deprecated Equals and StringScaled methods(#342) 2024-01-15 20:50:05 +01:00
zlasd
7b4ffbcd3c
Fix mod when the divisor is slightly greater than the half of dividend (#312) (#317) 2024-01-10 02:08:45 +01:00
zlasd
1df8fb3015
Ensure empty Decimal returns correct zero-value NumDigits (#301) 2023-12-31 13:11:33 +01:00
Jongmin Kim
572d78e32d
Fix examples for RoundDown, RoundFloor, RoundUp, and RoundCeil (#341) 2023-12-29 18:47:57 +01:00
mwoss
b4c250139b Update Go version in go.mod to match minimum version required from docs 2023-12-29 12:58:22 +01:00
Mateusz Woś
304c9d6794
Update Github Actions libraries to latest working version (#340)
* Update Github Actions libraries to the latest working version
* Update badge to show status from master branch only
2023-12-29 12:54:16 +01:00
Mateusz Woś
12e9241da2
Add implementation of natural logarithm (#339)
* Add initial implementation of natural logarithm
* Add constApproxmation struct to represent mathematical constants with their approximations
2023-12-29 01:59:48 +01:00
mwoss
88705b71ce Reformat documentation to current go standard 2023-12-28 21:05:09 +01:00
Vadim Kulagin
6926256b71
Allocate new(big.Int) in Copy method to deeply clone it (#278) 2023-12-27 15:53:43 +01:00
Geert van Kempen
aa8a681072 added sqrt testing 2018-08-19 14:00:50 -04:00
Geert van Kempen
3954520093 native square root function for decimal 2018-08-19 10:54:18 -04:00
Geert van Kempen
53965de086 additional decimal numbers 2018-08-19 10:44:54 -04:00
Geert van Kempen
8db667fde7 Merge branch 'patch-2' 2018-08-19 10:20:36 -04:00
kempeng
e20cd0e503
Create zero.go
Zero.go defines convenience functions for testing decimal values with the zero value. These functions make code using decimal easier to read in my opinion. EqualZero() could be renamed into IsZero()
2018-07-25 23:22:11 -04:00
kempeng
70eaa6c75d
Create helpers.go
helpers.go defines several helper functions for the create of new decimal's from int's that make code using decimal a bit easier to read. Further more it implements a.Max() and a.Min() functions in the same fashion as other arithmetic functions like Add & Sub. (the decimal package already implements Max(d, d2....) and Min(), but these two helper functions are complementary to them.
Finally a.Float() is a convenience function for a.Float64() that ignore the precise bool, allowing inline use of the float conversion function
2018-07-25 23:19:28 -04:00
12 changed files with 733 additions and 147 deletions

View file

@ -1,16 +1,16 @@
name: decimal-ci name: ci
on: [push] on: [push]
jobs: jobs:
ci-job: ci-job:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
go: [ '1.7.x', '1.15', '1.16', '1.17', '1.x' ] go: [ '1.7.x', '1.18', '1.19', '1.20', '1.21', '1.x' ]
name: Go ${{ matrix.go }} name: Go ${{ matrix.go }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Use go - name: Use go
uses: actions/setup-go@v2 uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- run: go build . - run: go build .

View file

@ -1,6 +1,6 @@
# decimal # decimal
[![Github Actions](https://github.com/shopspring/decimal/actions/workflows/ci.yml/badge.svg)](https://github.com/shopspring/decimal/actions/workflows/ci.yml) [![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) [![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) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal)

63
const.go Normal file
View file

@ -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]
}

34
const_test.go Normal file
View file

@ -0,0 +1,34 @@
package decimal
import "testing"
func TestConstApproximation(t *testing.T) {
for _, testCase := range []struct {
Const string
Precision int32
ExpectedApproximation string
}{
{"2.3025850929940456840179914546", 0, "2"},
{"2.3025850929940456840179914546", 1, "2.3"},
{"2.3025850929940456840179914546", 3, "2.302"},
{"2.3025850929940456840179914546", 5, "2.302585"},
{"2.3025850929940456840179914546", 10, "2.302585092994045"},
{"2.3025850929940456840179914546", 100, "2.3025850929940456840179914546"},
{"2.3025850929940456840179914546", -1, "2"},
{"2.3025850929940456840179914546", -5, "2"},
{"3.14159265359", 0, "3"},
{"3.14159265359", 1, "3.1"},
{"3.14159265359", 2, "3.141"},
{"3.14159265359", 4, "3.1415926"},
{"3.14159265359", 13, "3.14159265359"},
} {
ca := newConstApproximation(testCase.Const)
expected, _ := NewFromString(testCase.ExpectedApproximation)
approximation := ca.withPrecision(testCase.Precision)
if approximation.Cmp(expected) != 0 {
t.Errorf("expected approximation %s, got %s - for const with %s precision %d", testCase.ExpectedApproximation, approximation.String(), testCase.Const, testCase.Precision)
}
}
}

View file

@ -4,14 +4,14 @@
// //
// The best way to create a new Decimal is to use decimal.NewFromString, ex: // The best way to create a new Decimal is to use decimal.NewFromString, ex:
// //
// n, err := decimal.NewFromString("-123.4567") // n, err := decimal.NewFromString("-123.4567")
// n.String() // output: "-123.4567" // n.String() // output: "-123.4567"
// //
// To use Decimal as part of a struct: // To use Decimal as part of a struct:
// //
// type Struct struct { // type StructName struct {
// Number Decimal // Number Decimal
// } // }
// //
// Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point. // Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
package decimal package decimal
@ -32,16 +32,15 @@ import (
// //
// Example: // Example:
// //
// d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) // d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
// d1.String() // output: "0.6666666666666667" // d1.String() // output: "0.6666666666666667"
// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000)) // d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000))
// d2.String() // output: "0.0000666666666667" // d2.String() // output: "0.0000666666666667"
// d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3)) // d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3))
// d3.String() // output: "6666.6666666666666667" // d3.String() // output: "6666.6666666666666667"
// decimal.DivisionPrecision = 3 // decimal.DivisionPrecision = 3
// d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) // d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
// d4.String() // output: "0.667" // d4.String() // output: "0.667"
//
var DivisionPrecision = 16 var DivisionPrecision = 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
@ -91,12 +90,12 @@ func New(value int64, exp int32) Decimal {
} }
} }
// NewFromInt converts a int64 to Decimal. // NewFromInt converts an int64 to Decimal.
// //
// Example: // Example:
// //
// NewFromInt(123).String() // output: "123" // NewFromInt(123).String() // output: "123"
// NewFromInt(-10).String() // output: "-10" // NewFromInt(-10).String() // output: "-10"
func NewFromInt(value int64) Decimal { func NewFromInt(value int64) Decimal {
return Decimal{ return Decimal{
value: big.NewInt(value), value: big.NewInt(value),
@ -104,12 +103,12 @@ func NewFromInt(value int64) Decimal {
} }
} }
// NewFromInt32 converts a int32 to Decimal. // NewFromInt32 converts an int32 to Decimal.
// //
// Example: // Example:
// //
// NewFromInt(123).String() // output: "123" // NewFromInt(123).String() // output: "123"
// NewFromInt(-10).String() // output: "-10" // NewFromInt(-10).String() // output: "-10"
func NewFromInt32(value int32) Decimal { func NewFromInt32(value int32) Decimal {
return Decimal{ return Decimal{
value: big.NewInt(int64(value)), value: big.NewInt(int64(value)),
@ -125,15 +124,34 @@ 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. // NewFromString returns a new Decimal from a string representation.
// Trailing zeroes are not trimmed. // Trailing zeroes are not trimmed.
// //
// Example: // Example:
// //
// d, err := NewFromString("-123.45") // d, err := NewFromString("-123.45")
// d2, err := NewFromString(".0001") // d2, err := NewFromString(".0001")
// d3, err := NewFromString("1.47000") // d3, err := NewFromString("1.47000")
//
func NewFromString(value string) (Decimal, error) { func NewFromString(value string) (Decimal, error) {
originalInput := value originalInput := value
var intString string var intString string
@ -211,15 +229,14 @@ func NewFromString(value string) (Decimal, error) {
// //
// Example: // Example:
// //
// r := regexp.MustCompile("[$,]") // r := regexp.MustCompile("[$,]")
// d1, err := NewFromFormattedString("$5,125.99", r) // d1, err := NewFromFormattedString("$5,125.99", r)
// //
// r2 := regexp.MustCompile("[_]") // r2 := regexp.MustCompile("[_]")
// d2, err := NewFromFormattedString("1_000_000", r2) // d2, err := NewFromFormattedString("1_000_000", r2)
//
// r3 := regexp.MustCompile("[USD\\s]")
// d3, err := NewFromFormattedString("5000 USD", r3)
// //
// r3 := regexp.MustCompile("[USD\\s]")
// d3, err := NewFromFormattedString("5000 USD", r3)
func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error) { func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error) {
parsedValue := replRegexp.ReplaceAllString(value, "") parsedValue := replRegexp.ReplaceAllString(value, "")
d, err := NewFromString(parsedValue) d, err := NewFromString(parsedValue)
@ -230,13 +247,12 @@ func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, e
} }
// RequireFromString returns a new Decimal from a string representation // 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: // Example:
// //
// d := RequireFromString("-123.45") // d := RequireFromString("-123.45")
// d2 := RequireFromString(".0001") // d2 := RequireFromString(".0001")
//
func RequireFromString(value string) Decimal { func RequireFromString(value string) Decimal {
dec, err := NewFromString(value) dec, err := NewFromString(value)
if err != nil { if err != nil {
@ -332,8 +348,7 @@ func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal {
// //
// Example: // Example:
// //
// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46" // NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"
//
func NewFromFloatWithExponent(value float64, exp int32) Decimal { func NewFromFloatWithExponent(value float64, exp int32) Decimal {
if math.IsNaN(value) || math.IsInf(value, 0) { if math.IsNaN(value) || math.IsInf(value, 0) {
panic(fmt.Sprintf("Cannot create a Decimal from %v", value)) panic(fmt.Sprintf("Cannot create a Decimal from %v", value))
@ -418,7 +433,7 @@ func NewFromFloatWithExponent(value float64, exp int32) Decimal {
func (d Decimal) Copy() Decimal { func (d Decimal) Copy() Decimal {
d.ensureInitialized() d.ensureInitialized()
return Decimal{ return Decimal{
value: &(*d.value), value: new(big.Int).Set(d.value),
exp: d.exp, exp: d.exp,
} }
} }
@ -430,7 +445,7 @@ func (d Decimal) Copy() Decimal {
// //
// Example: // Example:
// //
// d := New(12345, -4) // d := New(12345, -4)
// d2 := d.rescale(-1) // d2 := d.rescale(-1)
// d3 := d2.rescale(-4) // d3 := d2.rescale(-4)
// println(d1) // println(d1)
@ -442,7 +457,6 @@ func (d Decimal) Copy() Decimal {
// 1.2345 // 1.2345
// 1.2 // 1.2
// 1.2000 // 1.2000
//
func (d Decimal) rescale(exp int32) Decimal { func (d Decimal) rescale(exp int32) Decimal {
d.ensureInitialized() d.ensureInitialized()
@ -554,9 +568,11 @@ func (d Decimal) Div(d2 Decimal) Decimal {
// QuoRem does division with remainder // QuoRem does division with remainder
// d.QuoRem(d2,precision) returns quotient q and remainder r such that // 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 // 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
// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0
//
// Note that precision<0 is allowed as input. // Note that precision<0 is allowed as input.
func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) { func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
d.ensureInitialized() d.ensureInitialized()
@ -565,7 +581,7 @@ func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
panic("decimal division by 0") panic("decimal division by 0")
} }
scale := -precision 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 { if e > math.MaxInt32 || e < math.MinInt32 {
panic("overflow in decimal QuoRem") panic("overflow in decimal QuoRem")
} }
@ -599,8 +615,10 @@ func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
// DivRound divides and rounds to a given precision // DivRound divides and rounds to a given precision
// i.e. to an integer multiple of 10^(-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. // Note that precision<0 is allowed as input.
func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal { func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal {
// QuoRem already checks initialization // QuoRem already checks initialization
@ -628,8 +646,8 @@ func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal {
// Mod returns d % d2. // Mod returns d % d2.
func (d Decimal) Mod(d2 Decimal) Decimal { func (d Decimal) Mod(d2 Decimal) Decimal {
quo := d.DivRound(d2, -d.exp+1).Truncate(0) _, r := d.QuoRem(d2, 0)
return d.Sub(d2.Mul(quo)) return r
} }
// Pow returns d to the power d2 // Pow returns d to the power d2
@ -655,9 +673,8 @@ func (d Decimal) Pow(d2 Decimal) Decimal {
// //
// Example: // Example:
// //
// NewFromFloat(26.1).ExpHullAbrham(2).String() // output: "220000000000" // NewFromFloat(26.1).ExpHullAbrham(2).String() // output: "220000000000"
// NewFromFloat(26.1).ExpHullAbrham(20).String() // output: "216314672147.05767284" // NewFromFloat(26.1).ExpHullAbrham(20).String() // output: "216314672147.05767284"
//
func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) { func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) {
// Algorithm based on Variable precision exponential function. // Algorithm based on Variable precision exponential function.
// ACM Transactions on Mathematical Software by T. E. Hull & A. Abrham. // ACM Transactions on Mathematical Software by T. E. Hull & A. Abrham.
@ -747,15 +764,14 @@ func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) {
// //
// Example: // Example:
// //
// d, err := NewFromFloat(26.1).ExpTaylor(2).String() // d, err := NewFromFloat(26.1).ExpTaylor(2).String()
// d.String() // output: "216314672147.06" // d.String() // output: "216314672147.06"
// //
// NewFromFloat(26.1).ExpTaylor(20).String() // NewFromFloat(26.1).ExpTaylor(20).String()
// d.String() // output: "216314672147.05767284062928674083" // d.String() // output: "216314672147.05767284062928674083"
//
// NewFromFloat(26.1).ExpTaylor(-10).String()
// d.String() // output: "220000000000"
// //
// NewFromFloat(26.1).ExpTaylor(-10).String()
// d.String() // output: "220000000000"
func (d Decimal) ExpTaylor(precision int32) (Decimal, error) { func (d Decimal) ExpTaylor(precision int32) (Decimal, error) {
// Note(mwoss): Implementation can be optimized by exclusively using big.Int API only // Note(mwoss): Implementation can be optimized by exclusively using big.Int API only
if d.IsZero() { if d.IsZero() {
@ -812,9 +828,127 @@ func (d Decimal) ExpTaylor(precision int32) (Decimal, error) {
return result, nil 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
for {
// 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 comp3.Abs().Cmp(epsilon) <= 0 {
break
}
}
}
comp1 = comp1.Add(reduceAdjust)
return comp1.Round(precision), nil
}
// 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 // 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()
// Note(mwoss): It can be optimized, unnecessary cast of big.Int to string // Note(mwoss): It can be optimized, unnecessary cast of big.Int to string
if d.IsNegative() { if d.IsNegative() {
return len(d.value.String()) - 1 return len(d.value.String()) - 1
@ -851,10 +985,9 @@ func abs(n int32) int32 {
// Cmp compares the numbers represented by d and d2 and returns: // Cmp compares the numbers represented by d and d2 and returns:
// //
// -1 if d < d2 // -1 if d < d2
// 0 if d == d2 // 0 if d == d2
// +1 if d > d2 // +1 if d > d2
//
func (d Decimal) Cmp(d2 Decimal) int { func (d Decimal) Cmp(d2 Decimal) int {
d.ensureInitialized() d.ensureInitialized()
d2.ensureInitialized() d2.ensureInitialized()
@ -868,12 +1001,21 @@ func (d Decimal) Cmp(d2 Decimal) int {
return rd.value.Cmp(rd2.value) 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. // Equal returns whether the numbers represented by d and d2 are equal.
func (d Decimal) Equal(d2 Decimal) bool { func (d Decimal) Equal(d2 Decimal) bool {
return d.Cmp(d2) == 0 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 { func (d Decimal) Equals(d2 Decimal) bool {
return d.Equal(d2) return d.Equal(d2)
} }
@ -905,7 +1047,6 @@ func (d Decimal) LessThanOrEqual(d2 Decimal) bool {
// -1 if d < 0 // -1 if d < 0
// 0 if d == 0 // 0 if d == 0
// +1 if d > 0 // +1 if d > 0
//
func (d Decimal) Sign() int { func (d Decimal) Sign() int {
if d.value == nil { if d.value == nil {
return 0 return 0
@ -1014,13 +1155,12 @@ func (d Decimal) InexactFloat64() float64 {
// //
// Example: // Example:
// //
// d := New(-12345, -3) // d := New(-12345, -3)
// println(d.String()) // println(d.String())
// //
// Output: // Output:
// //
// -12.345 // -12.345
//
func (d Decimal) String() string { func (d Decimal) String() string {
return d.string(true) return d.string(true)
} }
@ -1030,14 +1170,13 @@ func (d Decimal) String() string {
// //
// Example: // Example:
// //
// NewFromFloat(0).StringFixed(2) // output: "0.00" // NewFromFloat(0).StringFixed(2) // output: "0.00"
// NewFromFloat(0).StringFixed(0) // output: "0" // NewFromFloat(0).StringFixed(0) // output: "0"
// NewFromFloat(5.45).StringFixed(0) // output: "5" // NewFromFloat(5.45).StringFixed(0) // output: "5"
// NewFromFloat(5.45).StringFixed(1) // output: "5.5" // NewFromFloat(5.45).StringFixed(1) // output: "5.5"
// NewFromFloat(5.45).StringFixed(2) // output: "5.45" // NewFromFloat(5.45).StringFixed(2) // output: "5.45"
// NewFromFloat(5.45).StringFixed(3) // output: "5.450" // NewFromFloat(5.45).StringFixed(3) // output: "5.450"
// NewFromFloat(545).StringFixed(-1) // output: "550" // NewFromFloat(545).StringFixed(-1) // output: "550"
//
func (d Decimal) StringFixed(places int32) string { func (d Decimal) StringFixed(places int32) string {
rounded := d.Round(places) rounded := d.Round(places)
return rounded.string(false) return rounded.string(false)
@ -1048,14 +1187,13 @@ func (d Decimal) StringFixed(places int32) string {
// //
// Example: // Example:
// //
// NewFromFloat(0).StringFixedBank(2) // output: "0.00" // NewFromFloat(0).StringFixedBank(2) // output: "0.00"
// NewFromFloat(0).StringFixedBank(0) // output: "0" // NewFromFloat(0).StringFixedBank(0) // output: "0"
// NewFromFloat(5.45).StringFixedBank(0) // output: "5" // NewFromFloat(5.45).StringFixedBank(0) // output: "5"
// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4" // NewFromFloat(5.45).StringFixedBank(1) // output: "5.4"
// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45" // NewFromFloat(5.45).StringFixedBank(2) // output: "5.45"
// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450" // NewFromFloat(5.45).StringFixedBank(3) // output: "5.450"
// NewFromFloat(545).StringFixedBank(-1) // output: "540" // NewFromFloat(545).StringFixedBank(-1) // output: "540"
//
func (d Decimal) StringFixedBank(places int32) string { func (d Decimal) StringFixedBank(places int32) string {
rounded := d.RoundBank(places) rounded := d.RoundBank(places)
return rounded.string(false) return rounded.string(false)
@ -1073,9 +1211,8 @@ func (d Decimal) StringFixedCash(interval uint8) string {
// //
// Example: // Example:
// //
// NewFromFloat(5.45).Round(1).String() // output: "5.5" // NewFromFloat(5.45).Round(1).String() // output: "5.5"
// NewFromFloat(545).Round(-1).String() // output: "550" // NewFromFloat(545).Round(-1).String() // output: "550"
//
func (d Decimal) Round(places int32) Decimal { func (d Decimal) Round(places int32) Decimal {
if d.exp == -places { if d.exp == -places {
return d return d
@ -1104,11 +1241,10 @@ func (d Decimal) Round(places int32) Decimal {
// //
// Example: // Example:
// //
// NewFromFloat(545).RoundCeil(-2).String() // output: "600" // NewFromFloat(545).RoundCeil(-2).String() // output: "600"
// NewFromFloat(500).RoundCeil(-2).String() // output: "500" // NewFromFloat(500).RoundCeil(-2).String() // output: "500"
// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11" // NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11"
// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.5" // NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.4"
//
func (d Decimal) RoundCeil(places int32) Decimal { func (d Decimal) RoundCeil(places int32) Decimal {
if d.exp >= -places { if d.exp >= -places {
return d return d
@ -1130,11 +1266,10 @@ func (d Decimal) RoundCeil(places int32) Decimal {
// //
// Example: // Example:
// //
// NewFromFloat(545).RoundFloor(-2).String() // output: "500" // NewFromFloat(545).RoundFloor(-2).String() // output: "500"
// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500" // NewFromFloat(-500).RoundFloor(-2).String() // output: "-500"
// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1" // NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1"
// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.4" // NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.5"
//
func (d Decimal) RoundFloor(places int32) Decimal { func (d Decimal) RoundFloor(places int32) Decimal {
if d.exp >= -places { if d.exp >= -places {
return d return d
@ -1156,11 +1291,10 @@ func (d Decimal) RoundFloor(places int32) Decimal {
// //
// Example: // Example:
// //
// NewFromFloat(545).RoundUp(-2).String() // output: "600" // NewFromFloat(545).RoundUp(-2).String() // output: "600"
// NewFromFloat(500).RoundUp(-2).String() // output: "500" // NewFromFloat(500).RoundUp(-2).String() // output: "500"
// NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11" // NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11"
// NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.4" // NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.5"
//
func (d Decimal) RoundUp(places int32) Decimal { func (d Decimal) RoundUp(places int32) Decimal {
if d.exp >= -places { if d.exp >= -places {
return d return d
@ -1184,11 +1318,10 @@ func (d Decimal) RoundUp(places int32) Decimal {
// //
// Example: // Example:
// //
// NewFromFloat(545).RoundDown(-2).String() // output: "500" // NewFromFloat(545).RoundDown(-2).String() // output: "500"
// NewFromFloat(-500).RoundDown(-2).String() // output: "-500" // NewFromFloat(-500).RoundDown(-2).String() // output: "-500"
// NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1" // NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1"
// NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.5" // NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.4"
//
func (d Decimal) RoundDown(places int32) Decimal { func (d Decimal) RoundDown(places int32) Decimal {
if d.exp >= -places { if d.exp >= -places {
return d return d
@ -1209,13 +1342,12 @@ func (d Decimal) RoundDown(places int32) Decimal {
// //
// Examples: // Examples:
// //
// NewFromFloat(5.45).RoundBank(1).String() // output: "5.4" // NewFromFloat(5.45).RoundBank(1).String() // output: "5.4"
// NewFromFloat(545).RoundBank(-1).String() // output: "540" // NewFromFloat(545).RoundBank(-1).String() // output: "540"
// NewFromFloat(5.46).RoundBank(1).String() // output: "5.5" // NewFromFloat(5.46).RoundBank(1).String() // output: "5.5"
// NewFromFloat(546).RoundBank(-1).String() // output: "550" // NewFromFloat(546).RoundBank(-1).String() // output: "550"
// NewFromFloat(5.55).RoundBank(1).String() // output: "5.6" // NewFromFloat(5.55).RoundBank(1).String() // output: "5.6"
// NewFromFloat(555).RoundBank(-1).String() // output: "560" // NewFromFloat(555).RoundBank(-1).String() // output: "560"
//
func (d Decimal) RoundBank(places int32) Decimal { func (d Decimal) RoundBank(places int32) Decimal {
round := d.Round(places) round := d.Round(places)
@ -1237,11 +1369,13 @@ func (d Decimal) RoundBank(places int32) Decimal {
// interval. The amount payable for a cash transaction is rounded to the nearest // interval. The amount payable for a cash transaction is rounded to the nearest
// multiple of the minimum currency unit available. The following intervals are // multiple of the minimum currency unit available. The following intervals are
// available: 5, 10, 25, 50 and 100; any other number throws a panic. // 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) // 5: 5 cent rounding 3.43 => 3.45
// 25: 25 cent rounding 3.41 => 3.50 // 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up)
// 50: 50 cent rounding 3.75 => 4.00 // 25: 25 cent rounding 3.41 => 3.50
// 100: 100 cent rounding 3.50 => 4.00 // 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 // For more details: https://en.wikipedia.org/wiki/Cash_rounding
func (d Decimal) RoundCash(interval uint8) Decimal { func (d Decimal) RoundCash(interval uint8) Decimal {
var iVal *big.Int var iVal *big.Int
@ -1310,8 +1444,7 @@ func (d Decimal) Ceil() Decimal {
// //
// Example: // 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 { func (d Decimal) Truncate(precision int32) Decimal {
d.ensureInitialized() d.ensureInitialized()
if precision >= 0 && -precision > d.exp { if precision >= 0 && -precision > d.exp {
@ -1455,7 +1588,8 @@ func (d *Decimal) GobDecode(data []byte) error {
} }
// StringScaled first scales the decimal then calls .String() on it. // 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 { func (d Decimal) StringScaled(exp int32) string {
return d.rescale(exp).String() return d.rescale(exp).String()
} }
@ -1515,7 +1649,7 @@ func (d *Decimal) ensureInitialized() {
// //
// To call this function with an array, you must do: // 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. // This makes it harder to accidentally call Min with 0 arguments.
func Min(first Decimal, rest ...Decimal) Decimal { func Min(first Decimal, rest ...Decimal) Decimal {
@ -1532,7 +1666,7 @@ func Min(first Decimal, rest ...Decimal) Decimal {
// //
// To call this function with an array, you must do: // 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. // This makes it harder to accidentally call Max with 0 arguments.
func Max(first Decimal, rest ...Decimal) Decimal { func Max(first Decimal, rest ...Decimal) Decimal {
@ -1567,22 +1701,13 @@ func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) {
d1.ensureInitialized() d1.ensureInitialized()
d2.ensureInitialized() d2.ensureInitialized()
if d1.exp == d2.exp { if d1.exp < d2.exp {
return d1, d2 return d1, d2.rescale(d1.exp)
} else if d1.exp > d2.exp {
return d1.rescale(d2.exp), d2
} }
baseScale := min(d1.exp, d2.exp) return d1, d2
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
} }
func unquoteIfQuoted(value interface{}) (string, error) { func unquoteIfQuoted(value interface{}) (string, error) {
@ -1594,8 +1719,7 @@ func unquoteIfQuoted(value interface{}) (string, error) {
case []byte: case []byte:
bytes = v bytes = v
default: default:
return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'", return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'", value, value)
value, value)
} }
// If the amount is quoted, strip the quotes // If the amount is quoted, strip the quotes

View file

@ -556,10 +556,59 @@ func TestNewFromBigIntWithExponent(t *testing.T) {
} }
} }
func TestNewFromBigRat(t *testing.T) {
mustParseRat := func(val string) *big.Rat {
num, _ := new(big.Rat).SetString(val)
return num
}
type Inp struct {
val *big.Rat
prec int32
}
tests := map[Inp]string{
Inp{big.NewRat(0, 1), 16}: "0",
Inp{big.NewRat(4, 5), 16}: "0.8",
Inp{big.NewRat(10, 2), 16}: "5",
Inp{big.NewRat(1023427554493, 43432632), 16}: "23563.5628642767953828", // rounded
Inp{big.NewRat(1, 434324545566634), 16}: "0.0000000000000023",
Inp{big.NewRat(1, 3), 16}: "0.3333333333333333",
Inp{big.NewRat(2, 3), 2}: "0.67", // rounded
Inp{big.NewRat(2, 3), 16}: "0.6666666666666667", // rounded
Inp{big.NewRat(10000, 3), 16}: "3333.3333333333333333",
Inp{mustParseRat("30702832066636633479"), 16}: "30702832066636633479",
Inp{mustParseRat("487028320159896636679.1827512895753"), 16}: "487028320159896636679.1827512895753",
Inp{mustParseRat("127028320612589896636633479.173582751289575278357832"), -2}: "127028320612589896636633500", // rounded
Inp{mustParseRat("127028320612589896636633479.173582751289575278357832"), 16}: "127028320612589896636633479.1735827512895753", // rounded
Inp{mustParseRat("127028320612589896636633479.173582751289575278357832"), 32}: "127028320612589896636633479.173582751289575278357832",
}
// add negatives
for p, s := range tests {
if p.val.Cmp(new(big.Rat)) > 0 {
tests[Inp{p.val.Neg(p.val), p.prec}] = "-" + s
}
}
for input, s := range tests {
d := NewFromBigRat(input.val, input.prec)
if d.String() != s {
t.Errorf("expected %s, got %s (%s, %d)",
s, d.String(),
d.value.String(), d.exp)
}
}
}
func TestCopy(t *testing.T) { func TestCopy(t *testing.T) {
origin := New(1, 0) origin := New(1, 0)
cpy := origin.Copy() cpy := origin.Copy()
if origin.value == cpy.value {
t.Error("expecting copy and origin to have different value pointers")
}
if cpy.Cmp(origin) != 0 { if cpy.Cmp(origin) != 0 {
t.Error("expecting copy and origin to be equals, but they are not") t.Error("expecting copy and origin to be equals, but they are not")
} }
@ -2143,6 +2192,8 @@ func TestDecimal_Mod(t *testing.T) {
Inp{"-7.5", "2"}: "-1.5", Inp{"-7.5", "2"}: "-1.5",
Inp{"7.5", "-2"}: "1.5", Inp{"7.5", "-2"}: "1.5",
Inp{"-7.5", "-2"}: "-1.5", Inp{"-7.5", "-2"}: "-1.5",
Inp{"41", "21"}: "20",
Inp{"400000000001", "200000000001"}: "200000000000",
} }
for inp, res := range inputs { for inp, res := range inputs {
@ -2745,6 +2796,67 @@ func TestDecimal_ExpTaylor(t *testing.T) {
} }
} }
func TestDecimal_Ln(t *testing.T) {
for _, testCase := range []struct {
Dec string
Precision int32
Expected string
}{
{"0.1", 25, "-2.3025850929940456840179915"},
{"0.01", 25, "-4.6051701859880913680359829"},
{"0.001", 25, "-6.9077552789821370520539744"},
{"0.00000001", 25, "-18.4206807439523654721439316"},
{"1.0", 10, "0.0"},
{"1.01", 25, "0.0099503308531680828482154"},
{"1.001", 25, "0.0009995003330835331668094"},
{"1.0001", 25, "0.0000999950003333083353332"},
{"1.1", 25, "0.0953101798043248600439521"},
{"1.13", 25, "0.1222176327242492005461486"},
{"3.13", 10, "1.1410330046"},
{"3.13", 25, "1.1410330045520618486427824"},
{"3.13", 50, "1.14103300455206184864278239988848193837089629107972"},
{"3.13", 100, "1.1410330045520618486427823998884819383708962910797239760817078430268177216960996098918971117211892839"},
{"5.71", 25, "1.7422190236679188486939833"},
{"5.7185108151957193571930205", 50, "1.74370842450178929149992165925283704012576949094645"},
{"839101.0351", 25, "13.6400864014410013994397240"},
{"839101.0351094726488848490572028502", 50, "13.64008640145229044389152437468283605382056561604272"},
{"5023583755703750094849.03519358513093500275017501750602739169823", 25, "49.9684305274348922267409953"},
{"5023583755703750094849.03519358513093500275017501750602739169823", -1, "50.0"},
} {
d, _ := NewFromString(testCase.Dec)
expected, _ := NewFromString(testCase.Expected)
ln, err := d.Ln(testCase.Precision)
if err != nil {
t.Fatal(err)
}
if ln.Cmp(expected) != 0 {
t.Errorf("expected %s, got %s, for decimal %s", testCase.Expected, ln.String(), testCase.Dec)
}
}
}
func TestDecimal_LnZero(t *testing.T) {
d := New(0, 0)
_, err := d.Ln(5)
if err == nil {
t.Errorf("expected error, natural logarithm of 0 cannot be represented (-infinity)")
}
}
func TestDecimal_LnNegative(t *testing.T) {
d := New(-20, 2)
_, err := d.Ln(5)
if err == nil {
t.Errorf("expected error, natural logarithm cannot be calculated for nagative decimals")
}
}
func TestDecimal_NumDigits(t *testing.T) { func TestDecimal_NumDigits(t *testing.T) {
for _, testCase := range []struct { for _, testCase := range []struct {
Dec string Dec string
@ -2764,6 +2876,7 @@ func TestDecimal_NumDigits(t *testing.T) {
{"-5.26", 3}, {"-5.26", 3},
{"-5.2663117716", 11}, {"-5.2663117716", 11},
{"-26.1", 3}, {"-26.1", 3},
{"", 1},
} { } {
d, _ := NewFromString(testCase.Dec) d, _ := NewFromString(testCase.Dec)

2
go.mod
View file

@ -1,3 +1,3 @@
module github.com/shopspring/decimal module github.com/shopspring/decimal
go 1.13 go 1.7

38
helpers.go Normal file
View file

@ -0,0 +1,38 @@
package decimal
// Float returns the decimal value as a float64
// float is a helper function for float64() and doesnot return bool exact
func (d Decimal) Float() float64 {
f, _ := d.Float64()
return f
}
// NotEqual returns true when d is not equal to d2
func (d Decimal) NotEqual(d2 Decimal) bool {
return !d.Equal(d2)
}
// Max returns the maximum value between d and d2
func (d Decimal) Max(d2 Decimal) Decimal {
return Max(d, d2)
}
// Min returns the minimum value between d and d2
func (d Decimal) Min(d2 Decimal) Decimal {
return Min(d, d2)
}
//NewFromInt returns a decimal with the value of int v
func NewFromInt(v int) Decimal {
return New(int64(v), 0)
}
//NewFromInt32 returns a decimal with the value of int v
func NewFromInt32(v int) Decimal {
return New(int64(v), 0)
}
//NewFromInt64 returns a decimal with the value of int v
func NewFromInt64(v int64) Decimal {
return New(v, 0)
}

124
numbers.go Normal file
View file

@ -0,0 +1,124 @@
package decimal
// MinusOne defines value -1 in decimal format
var MinusOne = New(-1, 0)
// OneTenth defines value 0.1 in decimal format
var OneTenth = New(1, -1)
// TwoTenth defines value 0.1 in decimal format
var TwoTenth = New(2, -1)
// Half defines value 0.5 in decimal format
var Half = New(5, -1)
// One defines value 1 in decimal format
var One = New(1, 0)
// Two defines value 2 in decimal format
var Two = New(2, 0)
// Three defines value 3 in decimal format
var Three = New(3, 0)
// Four defines value 4 in decimal format
var Four = New(4, 0)
// Five defines value 5 in decimal format
var Five = New(5, 0)
// Six defines value 6 in decimal format
var Six = New(6, 0)
// Seven defines value 6 in decimal format
var Seven = New(7, 0)
// Eight defines value 8 in decimal format
var Eight = New(8, 0)
// Nine defines value 9 in decimal format
var Nine = New(9, 0)
// Ten defines value 10 in decimal format
var Ten = New(10, 0)
// Eleven defines value 11 in decimal format
var Eleven = New(11, 0)
// Twelve defines value 12 in decimal format
var Twelve = New(12, 0)
// Thirteen defines value 13 in decimal format
var Thirteen = New(13, 0)
// Fourteen defines value 14 in decimal format
var Fourteen = New(14, 0)
// Fifteen defines value 15 in decimal format
var Fifteen = New(15, 0)
// Sixteen defines value 16 in decimal format
var Sixteen = New(16, 0)
// Seventeen defines value 17 in decimal format
var Seventeen = New(17, 0)
// Eighteen defines value 18 in decimal format
var Eighteen = New(18, 0)
// Nineteen defines value 19 in decimal format
var Nineteen = New(19, 0)
// Twenty defines value 20 in decimal format
var Twenty = New(20, 0)
// Thirty defines value 30 in decimal format
var Thirty = New(30, 0)
// Forty defines value 40 in decimal format
var Forty = New(40, 0)
// Fifty defines value 50 in decimal format
var Fifty = New(50, 0)
// Sixty defines value 60 in decimal format
var Sixty = New(60, 0)
// Seventy defines value 70 in decimal format
var Seventy = New(70, 0)
// Eighty defines value 80 in decimal format
var Eighty = New(80, 0)
// Ninty defines value 90 in decimal format
var Ninty = New(90, 0)
// Hundred defines value 100 in decimal format
var Hundred = New(100, 0)
// TwoHundred defines value 200 in decimal format
var TwoHundred = New(200, 0)
// ThreeHundred defines value 300 in decimal format
var ThreeHundred = New(300, 0)
// FourHundred defines value 400 in decimal format
var FourHundred = New(400, 0)
// FiveHundred defines value 500 in decimal format
var FiveHundred = New(500, 0)
// SixHundred defines value 600 in decimal format
var SixHundred = New(600, 0)
// SevenHundred defines value 700 in decimal format
var SevenHundred = New(700, 0)
// EightHundred defines value 800 in decimal format
var EightHundred = New(800, 0)
// NineHundred defines value 900 in decimal format
var NineHundred = New(900, 0)
// Thousand defines value 100 in decimal format
var Thousand = New(1000, 0)

35
sqrt.go Normal file
View file

@ -0,0 +1,35 @@
package decimal
const sqrtMaxIter = 10000
// Sqrt returns the square root of d, the result will have
// DivisionPrecision digits after the decimal point.
func (d Decimal) Sqrt() Decimal {
s, _ := d.SqrtRound(int32(DivisionPrecision))
return s
}
// SqrtRound returns the square root of d, the result will have
// precision digits after the decimal point. The bool precise returns whether the precision was reached
func (d Decimal) SqrtRound(precision int32) (Decimal, bool) {
if d.LessThanOrEqualZero() {
return Zero, false
}
cutoff := New(1, -precision)
lo := Zero
hi := d
var mid Decimal
for i := 0; i < sqrtMaxIter; i++ {
//mid = (lo+hi)/2;
mid = lo.Add(hi).DivRound(Two, precision)
if mid.Mul(mid).Sub(d).Abs().LessThan(cutoff) {
return mid, true
}
if mid.Mul(mid).GreaterThan(d) {
hi = mid
} else {
lo = mid
}
}
return mid, false
}

24
sqrt_test.go Normal file
View file

@ -0,0 +1,24 @@
package decimal
import (
"testing"
)
func TestSqrt(t *testing.T) {
tables := []struct {
x Decimal
n Decimal
}{
{One, One},
{Four, Two},
{Sixteen, Four},
{Two, NewFromFloat(1.4142135623730951)},
}
for _, table := range tables {
result := table.x.Sqrt()
if result.NotEqual(table.n) {
t.Errorf("Sqrt of (%v) was incorrect, got: %v, want: %v.", table.x.String(), result.String(), table.n.String())
}
}
}

31
zero.go Normal file
View file

@ -0,0 +1,31 @@
package decimal
// EqualZero returns whether the numbers represented by d equals zero.
func (d Decimal) EqualZero() bool {
return d.Equal(Zero)
}
// NotZero returns whether d is not zero
func (d Decimal) NotZero() bool {
return !d.EqualZero()
}
// GreaterThanZero (GT0) returns true when d is greater than zero.
func (d Decimal) GreaterThanZero() bool {
return d.GreaterThan(Zero)
}
// GreaterThanOrEqualZero (GTE0) returns true when d is greater than or equal to zero.
func (d Decimal) GreaterThanOrEqualZero() bool {
return d.GreaterThanOrEqual(Zero)
}
// LessThanZero returns true when d is less than zero.
func (d Decimal) LessThanZero() bool {
return d.LessThan(Zero)
}
// LessThanOrEqualZero returns true when d is less than or equal to zero.
func (d Decimal) LessThanOrEqualZero() bool {
return d.LessThanOrEqual(Zero)
}