diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 1d618bd..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: ci -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - types: [ opened, synchronize ] -permissions: - contents: read -jobs: - ci-job: - runs-on: ubuntu-latest - strategy: - matrix: - go: [ '1.10.x', '1.24', '1.25', '1.26', '1.27', '1.x' ] - name: Go ${{ matrix.go }} - steps: - - uses: actions/checkout@v4 - - name: Use go - uses: actions/setup-go@v5 - with: - go-version: ${{ matrix.go }} - - run: go build . - - run: go test -v - lint: - runs-on: ubuntu-latest - name: Lint - steps: - - uses: actions/checkout@v4 - - name: Use go - uses: actions/setup-go@v5 - with: - go-version: stable - - uses: golangci/golangci-lint-action@v9 - with: - version: v2.14.0 diff --git a/.gitignore b/.gitignore index 6838f34..8a43ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,3 @@ # IntelliJ .idea/ *.iml - -# VS code -*.code-workspace -.vscode/ - -mise.toml \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index 794257a..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,64 +0,0 @@ -version: "2" - -# golangci-lint takes the Go version from go.mod (1.10), so linters that only suggest newer -# language features or APIs stay off: modernize, intrange, copyloopvar, and errorlint -# (errors.Is and %w need Go 1.13). - -linters: - default: standard # errcheck, govet, ineffassign, staticcheck, unused - enable: - - bidichk - - dupword - - gocritic - - makezero - - misspell - - nilerr - - nolintlint - - predeclared - - revive - - unconvert - - unparam - - wastedassign - # Off on purpose: - # - mnd: a math library is full of meaningful numeric constants. - # - gosec: its findings here are G115 integer conversions, which are deliberate and range checked. - # - funlen, gocyclo, gocognit, cyclop: numeric algorithms are long by nature. - settings: - govet: - enable-all: true - disable: - - fieldalignment # field order follows readability, not padding - - shadow # shadowing err and loop variables is common and harmless here - staticcheck: - checks: - # golangci-lint's defaults - - all - - -ST1000 - - -ST1003 - - -ST1016 - - -ST1020 - - -ST1021 - - -ST1022 - - -QF1004 # suggests strings.ReplaceAll, which needs Go 1.12 - gocritic: - # Bug and performance checks only. The style checks are opinionated, and the experimental - # commentedOutCode check reads formula comments like "v * 10^(f-q) = e^s * 10^f" as code. - enabled-tags: - - diagnostic - - performance - disabled-tags: - - style - - experimental - - opinionated - -issues: - max-issues-per-linter: 0 - max-same-issues: 0 - -formatters: - enable: - - gofmt - - goimports - settings: - gofmt: - simplify: false # gofmt -s would rewrite many test tables that open pull requests also change diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..55d42b2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: go + +go: + - 1.7.x + - 1.12.x + - 1.13.x + - tip + +install: + - go build . + +script: + - go test -v diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d3bad9..01ba02f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,124 +1,4 @@ -## Decimal v1.6.0 - -#### BEHAVIOR CHANGES -- `Decimal` implements `fmt.Formatter`. `%#v` prints Go code that recreates the value, like `decimal.New(314, -2)`, instead of the struct fields, and verbs that don't apply, like `%d`, print `%!d(decimal.Decimal=3.14)`. `%v`, `%s`, `%q`, `%x` and `fmt.Println` print the same as before [#463](https://github.com/shopspring/decimal/pull/463) -- `Ln` rounds correctly, so some results change in the last digit: `Ln` of 10 to 5 places is `2.30259`, not `2.30258` [#471](https://github.com/shopspring/decimal/pull/471) -- `ExpHullAbrham` returns `overallPrecision` significant figures for every input. Negative inputs used to lose digits or return 0, like `-50` at precision 10, and large inputs returned extra digits [#429](https://github.com/shopspring/decimal/pull/429) [#470](https://github.com/shopspring/decimal/pull/470) -- `Scan` converts `float32` with `NewFromFloat32`, so `54.33` stays `54.33` instead of becoming `54.33000183105469`. NaN and infinite floats return an error instead of panicking [#469](https://github.com/shopspring/decimal/pull/469) -- On Go 1.10 to 1.12, `NewFromFloat` can pick a different shortest form in rare cases ([golang/go#29491](https://github.com/golang/go/issues/29491)). It still converts back to the same float [#431](https://github.com/shopspring/decimal/pull/431) - -#### FEATURES -- Add BSON Decimal128 support for `Decimal` and `NullDecimal` with the official MongoDB Go driver v2, without a new dependency [#460](https://github.com/shopspring/decimal/pull/460) -- Add `%f` formatting with precision, width and flags, like `%.2f` and `%08.2f` [#463](https://github.com/shopspring/decimal/pull/463) -- Add `Normalize`, which strips trailing zeros so equal decimals also match with `reflect.DeepEqual`, testify and gomock [#464](https://github.com/shopspring/decimal/pull/464) -- Add `Sqrt` and `SqrtRound` [#465](https://github.com/shopspring/decimal/pull/465) -- Add `Root` and `RootRound` for n-th roots [#473](https://github.com/shopspring/decimal/pull/473) -- Add `NewFromBigFloat` constructor [#457](https://github.com/shopspring/decimal/pull/457) -- `Scan` accepts `Decimal`, `big.Int`, `*big.Int` and all integer sizes, as passed by ClickHouse and other drivers [#469](https://github.com/shopspring/decimal/pull/469) - -#### ENHANCEMENTS -- `NewFromFloat` and `NewFromFloat32` are 3 to 6 times faster. They use `strconv` instead of a vendored copy of it from 2009, which is removed [#431](https://github.com/shopspring/decimal/pull/431) -- `Ln` is 3 to 14 times faster [#471](https://github.com/shopspring/decimal/pull/471) -- Slightly faster `IsInteger` and `Ceil` [#299](https://github.com/shopspring/decimal/pull/299) -- `NewFromString` accepts the Unicode minus sign `−` (U+2212) [#428](https://github.com/shopspring/decimal/pull/428) -- Scanning NULL into a `Decimal` returns an error that points to `NullDecimal` or `*Decimal` [#474](https://github.com/shopspring/decimal/pull/474) -- Document that global settings aren't goroutine-safe, and list them with their per-call alternatives in the README [#466](https://github.com/shopspring/decimal/pull/466) -- README: show how to control the JSON format of one field, add `udecimal` to the alternative libraries, and update the Go doc and playground links [#475](https://github.com/shopspring/decimal/pull/475) [#476](https://github.com/shopspring/decimal/pull/476) [#264](https://github.com/shopspring/decimal/pull/264) -- Fix doc examples that were wrong or didn't compile [#468](https://github.com/shopspring/decimal/pull/468) -- Fix benchmarks that measured their setup or the wrong function [#467](https://github.com/shopspring/decimal/pull/467) - -#### BUGFIXES -- `QuoRem` and `DivRound` panic on precision `math.MinInt32` instead of returning a wrong quotient [#472](https://github.com/shopspring/decimal/pull/472) - -## Decimal v1.5.0 - -#### BEHAVIOR CHANGES -- `NewFromString` and `UnmarshalBinary`, and so JSON, text, SQL, gob and Spanner decoding, reject exponents larger than the new `MaxDecodeExponent` setting (default 10,000). Such input could hang the process or use gigabytes of memory. Raise `MaxDecodeExponent` if you need larger exponents [#440](https://github.com/shopspring/decimal/pull/440) -- `Pow`, `PowWithPrecision`, `PowInt32` and `PowBigInt` round correctly. Results for fractional exponents have `PowPrecisionNegativeExponent` (16) decimal places, and negative powers that used to return 0 keep 16 significant digits, so `10^-18` is `0.000000000000000001`. Results too large to represent return an error, or 0 from `Pow`, instead of hanging or panicking [#439](https://github.com/shopspring/decimal/pull/439) -- `RoundUp`, `RoundDown`, `RoundCeil` and `RoundFloor` always return exponent `-places`, like `Round`. Values don't change, but `Exponent()`, `Coefficient()` and `String()` with `TrimTrailingZeros = false` can [#426](https://github.com/shopspring/decimal/pull/426) [#441](https://github.com/shopspring/decimal/pull/441) -- `Truncate` supports negative precision: `5432` truncated to `-2` is `5400`. Before, it returned `5432` unchanged [#426](https://github.com/shopspring/decimal/pull/426) -- `Shift` panics when the exponent overflows an int32, instead of wrapping around [#437](https://github.com/shopspring/decimal/pull/437) -- `NewFromString` rejects a sign right after a leading decimal point, such as `".-5"` [#447](https://github.com/shopspring/decimal/pull/447) - -#### FEATURES -- Add `TrimTrailingZeros` and `UseScientificNotation` settings for string output [#389](https://github.com/shopspring/decimal/pull/389) [#416](https://github.com/shopspring/decimal/pull/416) -- Add Spanner support with `EncodeSpanner` and `DecodeSpanner` for `Decimal` and `NullDecimal` [#395](https://github.com/shopspring/decimal/pull/395) - -#### ENHANCEMENTS -- Most operations are faster and allocate less, about twice as fast on average across the benchmarks [#444](https://github.com/shopspring/decimal/pull/444) [#445](https://github.com/shopspring/decimal/pull/445) [#446](https://github.com/shopspring/decimal/pull/446) [#447](https://github.com/shopspring/decimal/pull/447) [#448](https://github.com/shopspring/decimal/pull/448) [#449](https://github.com/shopspring/decimal/pull/449) [#451](https://github.com/shopspring/decimal/pull/451) -- Faster `UnmarshalJSON`, `Scan` and `NewFromString` [#403](https://github.com/shopspring/decimal/pull/403) -- `ExpHullAbrham` raises its partial sum to a power by squaring. Large inputs that took minutes now take seconds [#450](https://github.com/shopspring/decimal/pull/450) -- Document that `NewFromFloat` returns the shortest decimal that converts back to the same float, and how to convert large whole numbers exactly [#442](https://github.com/shopspring/decimal/pull/442) -- Split `decimal.go` into `decimal.go`, `math.go` and `encoding.go` [#452](https://github.com/shopspring/decimal/pull/452) -- Lint with golangci-lint in CI, and test on Go 1.26 and 1.27 [#453](https://github.com/shopspring/decimal/pull/453) [#454](https://github.com/shopspring/decimal/pull/454) - -#### BUGFIXES -- Fix `Float64` hanging on decimals with huge exponents [#438](https://github.com/shopspring/decimal/pull/438) -- Fix `ExpHullAbrham` returning `10^exp` instead of 1 for tiny inputs [#433](https://github.com/shopspring/decimal/pull/433) -- Fix a data race in `ExpTaylor` [#414](https://github.com/shopspring/decimal/pull/414) -- Fix `NumDigits` for some exact powers of ten [#425](https://github.com/shopspring/decimal/pull/425) -- Fix the `omitzero` struct tag for `Decimal` fields in nested structs [#415](https://github.com/shopspring/decimal/pull/415) -- Fix `RoundCash` giving wrong results with a low `DivisionPrecision` [#443](https://github.com/shopspring/decimal/pull/443) -- `NullDecimal` is no longer `Valid` after a failed `Scan`, `UnmarshalJSON` or `DecodeSpanner` [#443](https://github.com/shopspring/decimal/pull/443) - -## Decimal v1.4.0 -#### BREAKING -- Drop support for Go version older than 1.10 [#361](https://github.com/shopspring/decimal/pull/361) - -#### FEATURES -- Add implementation of natural logarithm [#339](https://github.com/shopspring/decimal/pull/339) [#357](https://github.com/shopspring/decimal/pull/357) -- Add improved implementation of power operation [#358](https://github.com/shopspring/decimal/pull/358) -- Add Compare method which forwards calls to Cmp [#346](https://github.com/shopspring/decimal/pull/346) -- Add NewFromBigRat constructor [#288](https://github.com/shopspring/decimal/pull/288) -- Add NewFromUint64 constructor [#352](https://github.com/shopspring/decimal/pull/352) - -#### ENHANCEMENTS -- Migrate to Github Actions [#245](https://github.com/shopspring/decimal/pull/245) [#340](https://github.com/shopspring/decimal/pull/340) -- Fix examples for RoundDown, RoundFloor, RoundUp, and RoundCeil [#285](https://github.com/shopspring/decimal/pull/285) [#328](https://github.com/shopspring/decimal/pull/328) [#341](https://github.com/shopspring/decimal/pull/341) -- Use Godoc standard to mark deprecated Equals and StringScaled methods [#342](https://github.com/shopspring/decimal/pull/342) -- Removed unnecessary min function for RescalePair method [#265](https://github.com/shopspring/decimal/pull/265) -- Avoid reallocation of initial slice in MarshalBinary (GobEncode) [#355](https://github.com/shopspring/decimal/pull/355) -- Optimize NumDigits method [#301](https://github.com/shopspring/decimal/pull/301) [#356](https://github.com/shopspring/decimal/pull/356) -- Optimize BigInt method [#359](https://github.com/shopspring/decimal/pull/359) -- Support scanning uint64 [#131](https://github.com/shopspring/decimal/pull/131) [#364](https://github.com/shopspring/decimal/pull/364) -- Add docs section with alternative libraries [#363](https://github.com/shopspring/decimal/pull/363) - -#### BUGFIXES -- Fix incorrect calculation of decimal modulo [#258](https://github.com/shopspring/decimal/pull/258) [#317](https://github.com/shopspring/decimal/pull/317) -- Allocate new(big.Int) in Copy method to deeply clone it [#278](https://github.com/shopspring/decimal/pull/278) -- Fix overflow edge case in QuoRem method [#322](https://github.com/shopspring/decimal/pull/322) - -## Decimal v1.3.1 - -#### ENHANCEMENTS -- Reduce memory allocation in case of initialization from big.Int [#252](https://github.com/shopspring/decimal/pull/252) - -#### BUGFIXES -- Fix binary marshalling of decimal zero value [#253](https://github.com/shopspring/decimal/pull/253) - -## Decimal v1.3.0 - -#### FEATURES -- Add NewFromFormattedString initializer [#184](https://github.com/shopspring/decimal/pull/184) -- Add NewNullDecimal initializer [#234](https://github.com/shopspring/decimal/pull/234) -- Add implementation of natural exponent function (Taylor, Hull-Abraham) [#229](https://github.com/shopspring/decimal/pull/229) -- Add RoundUp, RoundDown, RoundCeil, RoundFloor methods [#196](https://github.com/shopspring/decimal/pull/196) [#202](https://github.com/shopspring/decimal/pull/202) [#220](https://github.com/shopspring/decimal/pull/220) -- Add XML support for NullDecimal [#192](https://github.com/shopspring/decimal/pull/192) -- Add IsInteger method [#179](https://github.com/shopspring/decimal/pull/179) -- Add Copy helper method [#123](https://github.com/shopspring/decimal/pull/123) -- Add InexactFloat64 helper method [#205](https://github.com/shopspring/decimal/pull/205) -- Add CoefficientInt64 helper method [#244](https://github.com/shopspring/decimal/pull/244) - -#### ENHANCEMENTS -- Performance optimization of NewFromString init method [#198](https://github.com/shopspring/decimal/pull/198) -- Performance optimization of Abs and Round methods [#240](https://github.com/shopspring/decimal/pull/240) -- Additional tests (CI) for ppc64le architecture [#188](https://github.com/shopspring/decimal/pull/188) - -#### BUGFIXES -- Fix rounding in FormatFloat fallback path (roundShortest method, fix taken from Go main repository) [#161](https://github.com/shopspring/decimal/pull/161) -- Add slice range checks to UnmarshalBinary method [#232](https://github.com/shopspring/decimal/pull/232) - -## Decimal v1.2.0 +## Decimal v1.2.0 #### BREAKING - Drop support for Go version older than 1.7 [#172](https://github.com/shopspring/decimal/pull/172) diff --git a/README.md b/README.md index 4b782f5..b70f901 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,10 @@ # decimal -[![ci](https://github.com/shopspring/decimal/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/shopspring/decimal/actions/workflows/ci.yml) -[![GoDoc](https://pkg.go.dev/badge/github.com/shopspring/decimal)](https://pkg.go.dev/github.com/shopspring/decimal) +[![Build Status](https://travis-ci.org/shopspring/decimal.png?branch=master)](https://travis-ci.org/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) Arbitrary-precision fixed-point decimal numbers in go. _Note:_ Decimal library can "only" represent numbers with a maximum of 2^31 digits after the decimal point. -Results that aren't exact, like `Div` and `Sqrt`, are rounded to 16 decimal places by default, and decoded input is -limited to exponents up to 10,000. See [Global settings](#global-settings). ## Features @@ -16,7 +13,6 @@ limited to exponents up to 10,000. See [Global settings](#global-settings). * Division with specified precision * Database/sql serialization/deserialization * JSON and XML serialization/deserialization - * MongoDB BSON serialization/deserialization as Decimal128, with the official Go driver v2 ## Install @@ -24,12 +20,7 @@ Run `go get github.com/shopspring/decimal` ## Requirements -Decimal library requires Go version `>=1.10` - -## Documentation - -https://pkg.go.dev/github.com/shopspring/decimal - +Decimal library requires Go version `>=1.7` ## Usage @@ -66,33 +57,14 @@ func main() { } ``` -## Global settings +## Documentation -These package-level variables change how some functions behave: +http://godoc.org/github.com/shopspring/decimal -| Variable | Default | What it changes | Per-call alternative | -|---|---|---|---| -| `DivisionPrecision` | `16` | Decimal places in `Div` and `Sqrt` results, and in `Avg`, `Atan` and `Tan` | `DivRound`, `SqrtRound` | -| `PowPrecisionNegativeExponent` | `16` | Decimal places in `Pow`, `PowInt32` and `PowBigInt` results for negative or fractional exponents | `PowWithPrecision` | -| `TrimTrailingZeros` | `true` | Whether `String`, and so JSON, text and SQL output, drops trailing zeros: `2.50` prints as `2.5` | `StringFixed` | -| `UseScientificNotation` | `false` | Whether `String` prints numbers stored with a positive exponent in scientific notation, like `1.2E3` | `StringFixed` | -| `MarshalJSONWithoutQuotes` | `false` | Whether JSON output is a number instead of a string | | -| `ExpMaxIterations` | `1000` | Iteration limit of `ExpHullAbrham` | | -| `MaxDecodeExponent` | `10000` | Largest exponent size that `NewFromString` and the decoders accept | | +## Production Usage -Set them once at program start. Changing one while other goroutines use the library is a data race. - -## Alternative libraries - -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 :) - -* [cockroachdb/apd](https://github.com/cockroachdb/apd) - arbitrary precision, mutable and rich API similar to `big.Int`, more performant than this library -* [alpacahq/alpacadecimal](https://github.com/alpacahq/alpacadecimal) - high performance, low precision (12 digits), fully compatible API with this library -* [govalues/decimal](https://github.com/govalues/decimal) - high performance, zero-allocation, low precision (19 digits) -* [quagmt/udecimal](https://github.com/quagmt/udecimal) - high performance, mostly zero-allocation, up to 19 digits after the decimal point -* [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 +* [Spring](https://shopspring.com/), since August 14, 2014. +* If you are using this in production, please let us know! ## FAQ @@ -101,7 +73,7 @@ Explore other alternatives to find the one that best fits your needs :) Because float64 (or any binary floating point type, actually) can't represent numbers such as `0.1` exactly. -Consider this code: https://go.dev/play/p/TQBd4yJe6B You might expect that +Consider this code: http://play.golang.org/p/TQBd4yJe6B You might expect that it prints out `10`, but it actually prints `9.999999999999831`. Over time, these small errors can really add up! @@ -116,7 +88,7 @@ out, the string output has to stop somewhere (let's say it stops at 3 decimal digits, for simplicity), so you'll get 0.333, 0.333, and 0.333. But where did the other 0.001 go? -Here's the above example as code: https://go.dev/play/p/lCZZs0w9KE +Here's the above example as code: http://play.golang.org/p/lCZZs0w9KE With Decimal, the strings being printed out represent the number exactly. So, if you have `x = y = 1/3` (with precision 3), they will actually be equal to @@ -141,7 +113,7 @@ modifies `a` and sets `z` as an alias for `a`, which they might not expect. It also modifies any other aliases to `a`. Here's an example of the subtle bugs you can introduce with big.Int's API: -https://go.dev/play/p/x2R_78pa8r +https://play.golang.org/p/x2R_78pa8r In contrast, it's difficult to make such mistakes with decimal. Decimals behave like other go numbers types: even though `a = b` will not deep copy @@ -151,22 +123,6 @@ this causes extra allocations, so Decimal is less performant. My assumption is that if you're using Decimals, you probably care more about correctness than performance. -#### How do I control the JSON format of one field? - -Wrap the field in a small type with its own `MarshalJSON`. For example, to always write money with 2 decimal places: - -```go -type Money struct{ decimal.Decimal } - -func (m Money) MarshalJSON() ([]byte, error) { - return []byte(`"` + m.StringFixed(2) + `"`), nil -} -``` - -A `Money` field marshals as `"12.50"`, other decimals keep their usual format, and unmarshaling still works through -the embedded `Decimal`. To write one field as a JSON number instead of a string, return `[]byte(m.String())`. -This is safer than the global settings, which change every decimal in the program. - ## License The MIT License (MIT) diff --git a/const.go b/const.go deleted file mode 100644 index f2f8647..0000000 --- a/const.go +++ /dev/null @@ -1,59 +0,0 @@ -package decimal - -import ( - "math/big" -) - -const ( - strLn10 = "2.302585092994045684017991454684364207601101488628772976033327900967572609677352480235997205089598298341967784042286248633409525465082806756666287369098781689482907208325554680843799894826233198528393505308965377732628846163366222287698219886746543667474404243274365155048934314939391479619404400222105101714174800368808401264708068556774321622835522011480466371565912137345074785694768346361679210180644507064800027750268491674655058685693567342067058113642922455440575892572420824131469568901675894025677631135691929203337658714166023010570308963457207544037084746994016826928280848118428931484852494864487192780967627127577539702766860595249671667418348570442250719796500471495105049221477656763693866297697952211071826454973477266242570942932258279850258550978526538320760672631716430950599508780752371033310119785754733154142180842754386359177811705430982748238504564801909561029929182431823752535770975053956518769751037497088869218020518933950723853920514463419726528728696511086257149219884997874887377134568620916705849807828059751193854445009978131146915934666241071846692310107598438319191292230792503747298650929009880391941702654416816335727555703151596113564846546190897042819763365836983716328982174407366009162177850541779276367731145041782137660111010731042397832521894898817597921798666394319523936855916447118246753245630912528778330963604262982153040874560927760726641354787576616262926568298704957954913954918049209069438580790032763017941503117866862092408537949861264933479354871737451675809537088281067452440105892444976479686075120275724181874989395971643105518848195288330746699317814634930000321200327765654130472621883970596794457943468343218395304414844803701305753674262153675579814770458031413637793236291560128185336498466942261465206459942072917119370602444929358037007718981097362533224548366988505528285966192805098447175198503666680874970496982273220244823343097169111136813588418696549323714996941979687803008850408979618598756579894836445212043698216415292987811742973332588607915912510967187510929248475023930572665446276200923068791518135803477701295593646298412366497023355174586195564772461857717369368404676577047874319780573853271810933883496338813069945569399346101090745616033312247949360455361849123333063704751724871276379140924398331810164737823379692265637682071706935846394531616949411701841938119405416449466111274712819705817783293841742231409930022911502362192186723337268385688273533371925103412930705632544426611429765388301822384091026198582888433587455960453004548370789052578473166283701953392231047527564998119228742789713715713228319641003422124210082180679525276689858180956119208391760721080919923461516952599099473782780648128058792731993893453415320185969711021407542282796298237068941764740642225757212455392526179373652434440560595336591539160312524480149313234572453879524389036839236450507881731359711238145323701508413491122324390927681724749607955799151363982881058285740538000653371655553014196332241918087621018204919492651483892" -) - -var zeroInt = big.NewInt(0) -var oneInt = big.NewInt(1) -var twoInt = big.NewInt(2) -var fourInt = big.NewInt(4) -var fiveInt = big.NewInt(5) -var tenInt = big.NewInt(10) -var twentyInt = big.NewInt(20) - -// pow10Table holds 10^0 ... 10^127, its values must not be modified. -var pow10Table = func() []*big.Int { - t := make([]*big.Int, 128) - t[0] = big.NewInt(1) - for i := 1; i < len(t); i++ { - t[i] = new(big.Int).Mul(t[i-1], tenInt) - } - return t -}() - -// pow10Uint64 holds 10^0 ... 10^19. -var pow10Uint64 = func() (t [20]uint64) { - t[0] = 1 - for i := 1; i < len(t); i++ { - t[i] = t[i-1] * 10 - } - return t -}() - -// float64Pow10 holds 10^0 ... 10^22, the powers of ten that float64 represents exactly. -var float64Pow10 = [...]float64{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, - 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22} - -// pow5Int64 holds 5^0 ... 5^22. -var pow5Int64 = func() (t [23]int64) { - t[0] = 1 - for i := 1; i < len(t); i++ { - t[i] = t[i-1] * 5 - } - return t -}() - -// pow10 returns 10^n for n >= 0, the result must not be modified. -func pow10(n int64) *big.Int { - if n < int64(len(pow10Table)) { - return pow10Table[n] - } - return new(big.Int).Exp(tenInt, big.NewInt(n), nil) -} - -const zeros = "0000000000000000000000000000000000000000000000000000000000000000" diff --git a/decimal-go.go b/decimal-go.go new file mode 100644 index 0000000..9958d69 --- /dev/null +++ b/decimal-go.go @@ -0,0 +1,415 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package decimal + +type decimal struct { + d [800]byte // digits, big-endian representation + nd int // number of digits used + dp int // decimal point + neg bool // negative flag + trunc bool // discarded nonzero digits beyond d[:nd] +} + +func (a *decimal) String() string { + n := 10 + a.nd + if a.dp > 0 { + n += a.dp + } + if a.dp < 0 { + n += -a.dp + } + + buf := make([]byte, n) + w := 0 + switch { + case a.nd == 0: + return "0" + + case a.dp <= 0: + // zeros fill space between decimal point and digits + buf[w] = '0' + w++ + buf[w] = '.' + w++ + w += digitZero(buf[w : w+-a.dp]) + w += copy(buf[w:], a.d[0:a.nd]) + + case a.dp < a.nd: + // decimal point in middle of digits + w += copy(buf[w:], a.d[0:a.dp]) + buf[w] = '.' + w++ + w += copy(buf[w:], a.d[a.dp:a.nd]) + + default: + // zeros fill space between digits and decimal point + w += copy(buf[w:], a.d[0:a.nd]) + w += digitZero(buf[w : w+a.dp-a.nd]) + } + return string(buf[0:w]) +} + +func digitZero(dst []byte) int { + for i := range dst { + dst[i] = '0' + } + return len(dst) +} + +// trim trailing zeros from number. +// (They are meaningless; the decimal point is tracked +// independent of the number of digits.) +func trim(a *decimal) { + for a.nd > 0 && a.d[a.nd-1] == '0' { + a.nd-- + } + if a.nd == 0 { + a.dp = 0 + } +} + +// Assign v to a. +func (a *decimal) Assign(v uint64) { + var buf [24]byte + + // Write reversed decimal in buf. + n := 0 + for v > 0 { + v1 := v / 10 + v -= 10 * v1 + buf[n] = byte(v + '0') + n++ + v = v1 + } + + // Reverse again to produce forward decimal in a.d. + a.nd = 0 + for n--; n >= 0; n-- { + a.d[a.nd] = buf[n] + a.nd++ + } + a.dp = a.nd + trim(a) +} + +// Maximum shift that we can do in one pass without overflow. +// A uint has 32 or 64 bits, and we have to be able to accommodate 9<> 63) +const maxShift = uintSize - 4 + +// Binary shift right (/ 2) by k bits. k <= maxShift to avoid overflow. +func rightShift(a *decimal, k uint) { + r := 0 // read pointer + w := 0 // write pointer + + // Pick up enough leading digits to cover first shift. + var n uint + for ; n>>k == 0; r++ { + if r >= a.nd { + if n == 0 { + // a == 0; shouldn't get here, but handle anyway. + a.nd = 0 + return + } + for n>>k == 0 { + n = n * 10 + r++ + } + break + } + c := uint(a.d[r]) + n = n*10 + c - '0' + } + a.dp -= r - 1 + + var mask uint = (1 << k) - 1 + + // Pick up a digit, put down a digit. + for ; r < a.nd; r++ { + c := uint(a.d[r]) + dig := n >> k + n &= mask + a.d[w] = byte(dig + '0') + w++ + n = n*10 + c - '0' + } + + // Put down extra digits. + for n > 0 { + dig := n >> k + n &= mask + if w < len(a.d) { + a.d[w] = byte(dig + '0') + w++ + } else if dig > 0 { + a.trunc = true + } + n = n * 10 + } + + a.nd = w + trim(a) +} + +// Cheat sheet for left shift: table indexed by shift count giving +// number of new digits that will be introduced by that shift. +// +// For example, leftcheats[4] = {2, "625"}. That means that +// if we are shifting by 4 (multiplying by 16), it will add 2 digits +// when the string prefix is "625" through "999", and one fewer digit +// if the string prefix is "000" through "624". +// +// Credit for this trick goes to Ken. + +type leftCheat struct { + delta int // number of new digits + cutoff string // minus one digit if original < a. +} + +var leftcheats = []leftCheat{ + // Leading digits of 1/2^i = 5^i. + // 5^23 is not an exact 64-bit floating point number, + // so have to use bc for the math. + // Go up to 60 to be large enough for 32bit and 64bit platforms. + /* + seq 60 | sed 's/^/5^/' | bc | + awk 'BEGIN{ print "\t{ 0, \"\" }," } + { + log2 = log(2)/log(10) + printf("\t{ %d, \"%s\" },\t// * %d\n", + int(log2*NR+1), $0, 2**NR) + }' + */ + {0, ""}, + {1, "5"}, // * 2 + {1, "25"}, // * 4 + {1, "125"}, // * 8 + {2, "625"}, // * 16 + {2, "3125"}, // * 32 + {2, "15625"}, // * 64 + {3, "78125"}, // * 128 + {3, "390625"}, // * 256 + {3, "1953125"}, // * 512 + {4, "9765625"}, // * 1024 + {4, "48828125"}, // * 2048 + {4, "244140625"}, // * 4096 + {4, "1220703125"}, // * 8192 + {5, "6103515625"}, // * 16384 + {5, "30517578125"}, // * 32768 + {5, "152587890625"}, // * 65536 + {6, "762939453125"}, // * 131072 + {6, "3814697265625"}, // * 262144 + {6, "19073486328125"}, // * 524288 + {7, "95367431640625"}, // * 1048576 + {7, "476837158203125"}, // * 2097152 + {7, "2384185791015625"}, // * 4194304 + {7, "11920928955078125"}, // * 8388608 + {8, "59604644775390625"}, // * 16777216 + {8, "298023223876953125"}, // * 33554432 + {8, "1490116119384765625"}, // * 67108864 + {9, "7450580596923828125"}, // * 134217728 + {9, "37252902984619140625"}, // * 268435456 + {9, "186264514923095703125"}, // * 536870912 + {10, "931322574615478515625"}, // * 1073741824 + {10, "4656612873077392578125"}, // * 2147483648 + {10, "23283064365386962890625"}, // * 4294967296 + {10, "116415321826934814453125"}, // * 8589934592 + {11, "582076609134674072265625"}, // * 17179869184 + {11, "2910383045673370361328125"}, // * 34359738368 + {11, "14551915228366851806640625"}, // * 68719476736 + {12, "72759576141834259033203125"}, // * 137438953472 + {12, "363797880709171295166015625"}, // * 274877906944 + {12, "1818989403545856475830078125"}, // * 549755813888 + {13, "9094947017729282379150390625"}, // * 1099511627776 + {13, "45474735088646411895751953125"}, // * 2199023255552 + {13, "227373675443232059478759765625"}, // * 4398046511104 + {13, "1136868377216160297393798828125"}, // * 8796093022208 + {14, "5684341886080801486968994140625"}, // * 17592186044416 + {14, "28421709430404007434844970703125"}, // * 35184372088832 + {14, "142108547152020037174224853515625"}, // * 70368744177664 + {15, "710542735760100185871124267578125"}, // * 140737488355328 + {15, "3552713678800500929355621337890625"}, // * 281474976710656 + {15, "17763568394002504646778106689453125"}, // * 562949953421312 + {16, "88817841970012523233890533447265625"}, // * 1125899906842624 + {16, "444089209850062616169452667236328125"}, // * 2251799813685248 + {16, "2220446049250313080847263336181640625"}, // * 4503599627370496 + {16, "11102230246251565404236316680908203125"}, // * 9007199254740992 + {17, "55511151231257827021181583404541015625"}, // * 18014398509481984 + {17, "277555756156289135105907917022705078125"}, // * 36028797018963968 + {17, "1387778780781445675529539585113525390625"}, // * 72057594037927936 + {18, "6938893903907228377647697925567626953125"}, // * 144115188075855872 + {18, "34694469519536141888238489627838134765625"}, // * 288230376151711744 + {18, "173472347597680709441192448139190673828125"}, // * 576460752303423488 + {19, "867361737988403547205962240695953369140625"}, // * 1152921504606846976 +} + +// Is the leading prefix of b lexicographically less than s? +func prefixIsLessThan(b []byte, s string) bool { + for i := 0; i < len(s); i++ { + if i >= len(b) { + return true + } + if b[i] != s[i] { + return b[i] < s[i] + } + } + return false +} + +// Binary shift left (* 2) by k bits. k <= maxShift to avoid overflow. +func leftShift(a *decimal, k uint) { + delta := leftcheats[k].delta + if prefixIsLessThan(a.d[0:a.nd], leftcheats[k].cutoff) { + delta-- + } + + r := a.nd // read index + w := a.nd + delta // write index + + // Pick up a digit, put down a digit. + var n uint + for r--; r >= 0; r-- { + n += (uint(a.d[r]) - '0') << k + quo := n / 10 + rem := n - 10*quo + w-- + if w < len(a.d) { + a.d[w] = byte(rem + '0') + } else if rem != 0 { + a.trunc = true + } + n = quo + } + + // Put down extra digits. + for n > 0 { + quo := n / 10 + rem := n - 10*quo + w-- + if w < len(a.d) { + a.d[w] = byte(rem + '0') + } else if rem != 0 { + a.trunc = true + } + n = quo + } + + a.nd += delta + if a.nd >= len(a.d) { + a.nd = len(a.d) + } + a.dp += delta + trim(a) +} + +// Binary shift left (k > 0) or right (k < 0). +func (a *decimal) Shift(k int) { + switch { + case a.nd == 0: + // nothing to do: a == 0 + case k > 0: + for k > maxShift { + leftShift(a, maxShift) + k -= maxShift + } + leftShift(a, uint(k)) + case k < 0: + for k < -maxShift { + rightShift(a, maxShift) + k += maxShift + } + rightShift(a, uint(-k)) + } +} + +// If we chop a at nd digits, should we round up? +func shouldRoundUp(a *decimal, nd int) bool { + if nd < 0 || nd >= a.nd { + return false + } + if a.d[nd] == '5' && nd+1 == a.nd { // exactly halfway - round to even + // if we truncated, a little higher than what's recorded - always round up + if a.trunc { + return true + } + return nd > 0 && (a.d[nd-1]-'0')%2 != 0 + } + // not halfway - digit tells all + return a.d[nd] >= '5' +} + +// Round a to nd digits (or fewer). +// If nd is zero, it means we're rounding +// just to the left of the digits, as in +// 0.09 -> 0.1. +func (a *decimal) Round(nd int) { + if nd < 0 || nd >= a.nd { + return + } + if shouldRoundUp(a, nd) { + a.RoundUp(nd) + } else { + a.RoundDown(nd) + } +} + +// Round a down to nd digits (or fewer). +func (a *decimal) RoundDown(nd int) { + if nd < 0 || nd >= a.nd { + return + } + a.nd = nd + trim(a) +} + +// Round a up to nd digits (or fewer). +func (a *decimal) RoundUp(nd int) { + if nd < 0 || nd >= a.nd { + return + } + + // round up + for i := nd - 1; i >= 0; i-- { + c := a.d[i] + if c < '9' { // can stop after this digit + a.d[i]++ + a.nd = i + 1 + return + } + } + + // Number is all 9s. + // Change to single 1 with adjusted decimal point. + a.d[0] = '1' + a.nd = 1 + a.dp++ +} + +// Extract integer part, rounded appropriately. +// No guarantees about overflow. +func (a *decimal) RoundedInteger() uint64 { + if a.dp > 20 { + return 0xFFFFFFFFFFFFFFFF + } + var i int + n := uint64(0) + for i = 0; i < a.dp && i < a.nd; i++ { + n = n*10 + uint64(a.d[i]-'0') + } + for ; i < a.dp; i++ { + n *= 10 + } + if shouldRoundUp(a, a.dp) { + n++ + } + return n +} diff --git a/decimal.go b/decimal.go index 90a17e3..801c1a0 100644 --- a/decimal.go +++ b/decimal.go @@ -4,120 +4,64 @@ // // The best way to create a new Decimal is to use decimal.NewFromString, ex: // -// n, err := decimal.NewFromString("-123.4567") -// n.String() // output: "-123.4567" +// n, err := decimal.NewFromString("-123.4567") +// n.String() // output: "-123.4567" // // To use Decimal as part of a struct: // -// type StructName struct { -// Number Decimal -// } +// type Struct struct { +// Number Decimal +// } // // Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point. package decimal import ( + "database/sql/driver" + "encoding/binary" "fmt" - "io" "math" "math/big" - "math/bits" - "regexp" "strconv" "strings" ) // DivisionPrecision is the number of decimal places in the result when it -// doesn't divide exactly. Div and Sqrt read it, and so do Avg, Atan and Tan through Div. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. -// To choose the precision per call, use DivRound instead of Div and SqrtRound instead of Sqrt. +// doesn't divide exactly. // // Example: // -// d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) -// d1.String() // output: "0.6666666666666667" -// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000)) -// d2.String() // output: "0.0000666666666667" -// d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3)) -// d3.String() // output: "6666.6666666666666667" -// decimal.DivisionPrecision = 3 -// d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) -// d4.String() // output: "0.667" +// d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) +// d1.String() // output: "0.6666666666666667" +// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000)) +// d2.String() // output: "0.0000666666666667" +// d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3)) +// d3.String() // output: "6666.6666666666666667" +// decimal.DivisionPrecision = 3 +// d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3)) +// d4.String() // output: "0.667" +// var DivisionPrecision = 16 -// PowPrecisionNegativeExponent specifies the number of digits after the decimal point in the results of -// Pow, PowInt32 and PowBigInt for negative or non-integer exponents. A result that would round to 0 keeps -// PowPrecisionNegativeExponent significant digits instead (at least one). -// PowWithPrecision is not affected, it takes the precision as an argument. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. -// To choose the precision per call, use PowWithPrecision. -// -// Example: -// -// d1, err := decimal.NewFromFloat(15.2).PowInt32(-2) -// d1.String() // output: "0.0043282548476454" -// -// decimal.PowPrecisionNegativeExponent = 24 -// d2, err := decimal.NewFromFloat(15.2).PowInt32(-2) -// d2.String() // output: "0.004328254847645429362881" -var PowPrecisionNegativeExponent = 16 - // MarshalJSONWithoutQuotes should be set to true if you want the decimal to // be JSON marshaled as a number, instead of as a string. // WARNING: this is dangerous for decimals with many digits, since many JSON // unmarshallers (ex: Javascript's) will unmarshal JSON numbers to IEEE 754 // double-precision floating point numbers, which means you can potentially // silently lose precision. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. var MarshalJSONWithoutQuotes = false -// TrimTrailingZeros specifies whether trailing zeroes should be trimmed from a string representation of decimal. -// If set to true, trailing zeroes will be truncated (2.00 -> 2, 3.11 -> 3.11, 13.000 -> 13), -// otherwise trailing zeroes will be preserved (2.00 -> 2.00, 3.11 -> 3.11, 13.000 -> 13.000). -// Setting this value to false can be useful for APIs where exact decimal string representation matters. -// It affects String and everything built on it, such as MarshalJSON, MarshalText and Value. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. -// For a fixed number of decimal places per call, use StringFixed. -var TrimTrailingZeros = true - -// UseScientificNotation specifies whether scientific notation should be used when a decimal is turned -// into a string that has a "negative" precision. -// -// For example, 1200 rounded to the nearest 100 cannot accurately be shown as "1200" because the last two -// digits are unknown. With this set to true, that number would be expressed as "1.2E3" instead. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. -// StringFixed never uses scientific notation. -var UseScientificNotation = false - -// ExpMaxIterations specifies the maximum number of iterations needed to calculate -// precise natural exponent value using ExpHullAbrham method. -// -// Set it once at program start. Changing it while other goroutines use the package is a data race. -var ExpMaxIterations = 1000 - -// MaxDecodeExponent is the largest exponent magnitude that NewFromString and UnmarshalBinary accept, -// and so UnmarshalJSON, UnmarshalText, Scan, GobDecode, DecodeSpanner, UnmarshalBSONValue and their -// NullDecimal variants too. -// Operations like String, Add and Float64 take time and memory that grow with the exponent, so without -// this limit a short input like "1e-2000000000" can stall the process or exhaust its memory. -// The default covers the float64 and IEEE 754 decimal128 exponent ranges. -// -// Values created with New, NewFromBigInt or arithmetic are not checked, so they may not decode back from -// their own String or MarshalBinary output. The limit does not bound the cost of Pow or ExpTaylor -// arguments or of very long inputs; validate those separately. -// -// Set it once at program start, before any decoding. Changing it while other goroutines decode is a -// data race. Raise it only for trusted input. -var MaxDecodeExponent = 10000 - // Zero constant, to make computations faster. // Zero should never be compared with == or != directly, please use decimal.Equal or decimal.Cmp instead. -var Zero = Decimal{} +var Zero = New(0, 1) + +var zeroInt = big.NewInt(0) +var oneInt = big.NewInt(1) +var twoInt = big.NewInt(2) +var fourInt = big.NewInt(4) +var fiveInt = big.NewInt(5) +var tenInt = big.NewInt(10) +var twentyInt = big.NewInt(20) // Decimal represents a fixed-point decimal. It is immutable. // number = value * 10 ^ exp @@ -132,13 +76,6 @@ type Decimal struct { exp int32 } -func (d Decimal) getValue() *big.Int { - if d.value == nil { - return zeroInt - } - return d.value -} - // New returns a new fixed-point decimal, value * 10 ^ exp. func New(value int64, exp int32) Decimal { return Decimal{ @@ -147,12 +84,12 @@ func New(value int64, exp int32) Decimal { } } -// NewFromInt converts an int64 to Decimal. +// NewFromInt converts a int64 to Decimal. // // Example: // -// NewFromInt(123).String() // output: "123" -// NewFromInt(-10).String() // output: "-10" +// NewFromInt(123).String() // output: "123" +// NewFromInt(-10).String() // output: "-10" func NewFromInt(value int64) Decimal { return Decimal{ value: big.NewInt(value), @@ -160,12 +97,12 @@ func NewFromInt(value int64) Decimal { } } -// NewFromInt32 converts an int32 to Decimal. +// NewFromInt32 converts a int32 to Decimal. // // Example: // -// NewFromInt(123).String() // output: "123" -// NewFromInt(-10).String() // output: "-10" +// NewFromInt(123).String() // output: "123" +// NewFromInt(-10).String() // output: "-10" func NewFromInt32(value int32) Decimal { return Decimal{ value: big.NewInt(int64(value)), @@ -173,102 +110,30 @@ func NewFromInt32(value int32) Decimal { } } -// NewFromUint64 converts an uint64 to Decimal. -// -// Example: -// -// NewFromUint64(123).String() // output: "123" -func NewFromUint64(value uint64) Decimal { - return Decimal{ - value: new(big.Int).SetUint64(value), - exp: 0, - } -} - // NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp func NewFromBigInt(value *big.Int, exp int32) Decimal { return Decimal{ - value: new(big.Int).Set(value), + value: big.NewInt(0).Set(value), exp: exp, } } -// 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) -} - -// NewFromBigFloat returns a new Decimal from a big.Float. The result is the shortest decimal -// that rounds back to value at value's precision, as NewFromFloat does for a float64. -// It returns an error for nil and infinite values, and for exponents beyond MaxDecodeExponent. -// -// Example: -// -// f, _ := new(big.Float).SetString("-123.45") -// d, err := NewFromBigFloat(f) -// d.String() // output: "-123.45" -func NewFromBigFloat(value *big.Float) (Decimal, error) { - if value == nil { - return Decimal{}, fmt.Errorf("can't convert nil *big.Float to decimal") - } - if value.IsInf() { - return Decimal{}, fmt.Errorf("can't convert infinite *big.Float to decimal") - } - if value.Sign() == 0 { - return New(0, 0), nil - } - return NewFromString(value.Text('g', -1)) -} - // NewFromString returns a new Decimal from a string representation. // Trailing zeroes are not trimmed. // // Example: // -// d, err := NewFromString("-123.45") -// d2, err := NewFromString(".0001") -// d3, err := NewFromString("1.47000") +// d, err := NewFromString("-123.45") +// d2, err := NewFromString(".0001") +// d3, err := NewFromString("1.47000") +// func NewFromString(value string) (Decimal, error) { originalInput := value + var intString string var exp int64 - // Accept Unicode minus sign (U+2212) in addition to ASCII '-'. - value = strings.Replace(value, "\u2212", "-", -1) - - // Check if number is using scientific notation and find dots - eIndex := -1 - pIndex := -1 - for i, r := range value { - if r == 'E' || r == 'e' { - if eIndex > -1 { - return Decimal{}, fmt.Errorf("can't convert %s to decimal: multiple 'E' characters found", value) - } - eIndex = i - continue - } - - if r == '.' { - if pIndex > -1 { - return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value) - } - pIndex = i - } - } - + // Check if number is using scientific notation + eIndex := strings.IndexAny(value, "Ee") if eIndex != -1 { expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32) if err != nil { @@ -281,44 +146,29 @@ func NewFromString(value string) (Decimal, error) { exp = expInt } - numLen := len(value) - if pIndex != -1 { - if pIndex+1 < len(value) && (value[pIndex+1] == '-' || value[pIndex+1] == '+') { - // ParseInt and SetString would accept the sign once the point is removed, as in ".-5" - return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) - } - numLen-- - expInt := -len(value[pIndex+1:]) + parts := strings.Split(value, ".") + if len(parts) == 1 { + // There is no decimal point, we can just parse the original string as + // an int + intString = value + } else if len(parts) == 2 { + intString = parts[0] + parts[1] + expInt := -len(parts[1]) exp += int64(expInt) + } else { + return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value) } - var dValue *big.Int - // parsing in an int64 is faster than new(big.Int).SetString so this is just a shortcut for strings we know won't overflow - if numLen <= 18 { - parsed64, ok := parseInt64SkipIndex(value, pIndex) - if !ok { - return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) - } - dValue = big.NewInt(parsed64) - } else { - intString := value - if pIndex != -1 { - intString = value[:pIndex] + value[pIndex+1:] - } - dValue = new(big.Int) - _, ok := dValue.SetString(intString, 10) - if !ok { - return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) - } + dValue := new(big.Int) + _, ok := dValue.SetString(intString, 10) + if !ok { + return Decimal{}, fmt.Errorf("can't convert %s to decimal", value) } if exp < math.MinInt32 || exp > math.MaxInt32 { // NOTE(vadim): I doubt a string could realistically be this long return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput) } - if exp > int64(MaxDecodeExponent) || exp < -int64(MaxDecodeExponent) { - return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent %d exceeds MaxDecodeExponent (%d)", originalInput, exp, MaxDecodeExponent) - } return Decimal{ value: dValue, @@ -326,62 +176,14 @@ func NewFromString(value string) (Decimal, error) { }, nil } -// parseInt64SkipIndex parses s without the byte at index skip as strconv.ParseInt(s, 10, 64) would, -// s must have at most 18 other bytes so that the result can't overflow. -func parseInt64SkipIndex(s string, skip int) (int64, bool) { - i, neg := 0, false - if len(s) > 0 && skip != 0 && (s[0] == '+' || s[0] == '-') { - i, neg = 1, s[0] == '-' - } - var n int64 - digits := 0 - for ; i < len(s); i++ { - if i == skip { - continue - } - c := s[i] - if c < '0' || c > '9' { - return 0, false - } - n = n*10 + int64(c-'0') - digits++ - } - if neg { - n = -n - } - return n, digits > 0 -} - -// NewFromFormattedString returns a new Decimal from a formatted string representation. -// The second argument - replRegexp, is a regular expression that is used to find characters that should be -// removed from given decimal string representation. All matched characters will be replaced with an empty string. -// -// Example: -// -// r := regexp.MustCompile("[$,]") -// d1, err := NewFromFormattedString("$5,125.99", r) -// -// r2 := regexp.MustCompile("[_]") -// d2, err := NewFromFormattedString("1_000_000", r2) -// -// r3 := regexp.MustCompile("[USD\\s]") -// d3, err := NewFromFormattedString("5000 USD", r3) -func NewFromFormattedString(value string, replRegexp *regexp.Regexp) (Decimal, error) { - parsedValue := replRegexp.ReplaceAllString(value, "") - d, err := NewFromString(parsedValue) - if err != nil { - return Decimal{}, err - } - return d, nil -} - // RequireFromString returns a new Decimal from a string representation -// or panics if NewFromString had returned an error. +// or panics if NewFromString would have returned an error. // // Example: // -// d := RequireFromString("-123.45") -// d2 := RequireFromString(".0001") +// d := RequireFromString("-123.45") +// d2 := RequireFromString(".0001") +// func RequireFromString(value string) Decimal { dec, err := NewFromString(value) if err != nil { @@ -392,16 +194,11 @@ func RequireFromString(value string) Decimal { // NewFromFloat converts a float64 to Decimal. // -// The result is the shortest decimal that converts back to the same float64, -// the same number that strconv.FormatFloat(value, 'f', -1, 64) prints. +// The converted number will contain the number of significant digits that can be +// represented in a float with reliable roundtrip. // This is typically 15 digits, but may be more in some cases. // See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information. // -// This is not always the exact value stored in the float. Whole numbers larger -// than 2^53 can change: float64(1<<62) is exactly 4611686018427387904, but -// NewFromFloat returns 4611686018427388000. To convert whole numbers exactly, -// use NewFromFloatWithExponent(value, 0). -// // For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms. // // NOTE: this will panic on NaN, +/-inf @@ -409,18 +206,16 @@ func NewFromFloat(value float64) Decimal { if value == 0 { return New(0, 0) } - return newFromFloat(value, 64) + return newFromFloat(value, math.Float64bits(value), &float64info) } // NewFromFloat32 converts a float32 to Decimal. // -// The result is the shortest decimal that converts back to the same float32. +// The converted number will contain the number of significant digits that can be +// represented in a float with reliable roundtrip. // This is typically 6-8 digits depending on the input. // See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information. // -// As with NewFromFloat, whole numbers larger than 2^24 can change. To convert -// them exactly, use NewFromFloatWithExponent(float64(value), 0). -// // For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms. // // NOTE: this will panic on NaN, +/-inf @@ -428,66 +223,55 @@ func NewFromFloat32(value float32) Decimal { if value == 0 { return New(0, 0) } - return newFromFloat(float64(value), 32) + // XOR is workaround for https://github.com/golang/go/issues/26285 + a := math.Float32bits(value) ^ 0x80808080 + return newFromFloat(float64(value), uint64(a)^0x80808080, &float32info) } -// newFromFloat converts val to a Decimal holding the shortest decimal digit -// string that round-trips back to val when parsed at the given bit size. -// -// strconv already computes exactly that string, so we ask it for the value in -// 'e' format with precision -1 and read the digits back out. The 'e' layout is -// [-]d[.ddd]e±dd, which is bounded (at most 24 bytes) and trivial to scan, -// unlike 'f' which can run to over 750 bytes for extreme exponents. -func newFromFloat(val float64, bitSize int) Decimal { +func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal { if math.IsNaN(val) || math.IsInf(val, 0) { panic(fmt.Sprintf("Cannot create a Decimal from %v", val)) } + exp := int(bits>>flt.mantbits) & (1<>(flt.expbits+flt.mantbits) != 0 - // The shortest round-trip form has at most 17 significant digits for a - // float64 and 9 for a float32, so the significand always fits in an int64. - var mant int64 - nd := 0 - for _, c := range digits { - if c == '.' { - continue + roundShortest(&d, mant, exp, flt) + // If less than 19 digits, we can do calculation in an int64. + if d.nd < 19 { + tmp := int64(0) + m := int64(1) + for i := d.nd - 1; i >= 0; i-- { + tmp += m * int64(d.d[i]-'0') + m *= 10 } - mant = mant*10 + int64(c-'0') - nd++ + if d.neg { + tmp *= -1 + } + return Decimal{value: big.NewInt(tmp), exp: int32(d.dp) - int32(d.nd)} } - if neg { - mant = -mant + dValue := new(big.Int) + dValue, ok := dValue.SetString(string(d.d[:d.nd]), 10) + if ok { + return Decimal{value: dValue, exp: int32(d.dp) - int32(d.nd)} } - esign := 1 - switch expDigits[0] { - case '-': - esign = -1 - expDigits = expDigits[1:] - case '+': - expDigits = expDigits[1:] - } - e := 0 - for _, c := range expDigits { - e = e*10 + int(c-'0') - } - - // digits is d.ddd, i.e. the significand scaled by 10^(nd-1). - return Decimal{value: big.NewInt(mant), exp: int32(esign*e - nd + 1)} + return NewFromFloatWithExponent(val, int32(d.dp)-int32(d.nd)) } // NewFromFloatWithExponent converts a float64 to Decimal, with an arbitrary @@ -495,7 +279,8 @@ func newFromFloat(val float64, bitSize int) Decimal { // // Example: // -// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46" +// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46" +// func NewFromFloatWithExponent(value float64, exp int32) Decimal { if math.IsNaN(value) || math.IsInf(value, 0) { panic(fmt.Sprintf("Cannot create a Decimal from %v", value)) @@ -576,14 +361,6 @@ func NewFromFloatWithExponent(value float64, exp int32) Decimal { } } -// Copy returns a copy of decimal with the same value and exponent, but a different pointer to value. -func (d Decimal) Copy() Decimal { - return Decimal{ - value: new(big.Int).Set(d.getValue()), - exp: d.exp, - } -} - // rescale returns a rescaled version of the decimal. Returned // decimal may be less precise if the given exponent is bigger // than the initial exponent of the Decimal. @@ -591,38 +368,38 @@ func (d Decimal) Copy() Decimal { // // Example: // -// d1 := New(12345, -4) -// d2 := d1.rescale(-1) +// d := New(12345, -4) +// d2 := d.rescale(-1) // d3 := d2.rescale(-4) -// fmt.Println(d1) -// fmt.Println(d2) -// fmt.Println(d3) +// println(d1) +// println(d2) +// println(d3) // // Output: // // 1.2345 // 1.2 -// 1.2 +// 1.2000 +// func (d Decimal) rescale(exp int32) Decimal { + d.ensureInitialized() + if d.exp == exp { return Decimal{ - new(big.Int).Set(d.getValue()), + new(big.Int).Set(d.value), d.exp, } } // NOTE(vadim): must convert exps to float64 before - to prevent overflow diff := math.Abs(float64(exp) - float64(d.exp)) - value := new(big.Int) + value := new(big.Int).Set(d.value) + + expScale := new(big.Int).Exp(tenInt, big.NewInt(int64(diff)), nil) if exp > d.exp { - value.Quo(d.getValue(), pow10(int64(diff))) - if value.Sign() == 0 && d.getValue().Sign() != 0 { - // reflect.DeepEqual tells an empty word slice from nil, keep the empty one that - // dividing a copy of d.value in place used to leave - value.SetBits([]big.Word{}) - } - } else { - value.Mul(d.getValue(), pow10(int64(diff))) + value = value.Quo(value, expScale) + } else if exp < d.exp { + value = value.Mul(value, expScale) } return Decimal{ @@ -633,10 +410,8 @@ func (d Decimal) rescale(exp int32) Decimal { // Abs returns the absolute value of the decimal. func (d Decimal) Abs() Decimal { - if !d.IsNegative() { - return d - } - d2Value := new(big.Int).Abs(d.getValue()) + d.ensureInitialized() + d2Value := new(big.Int).Abs(d.value) return Decimal{ value: d2Value, exp: d.exp, @@ -645,47 +420,30 @@ func (d Decimal) Abs() Decimal { // Add returns d + d2. func (d Decimal) Add(d2 Decimal) Decimal { - // rescale returns a new value, so the sum can be stored in it - if d.exp < d2.exp { - r := d2.rescale(d.exp) - r.value.Add(d.getValue(), r.value) - return r - } else if d.exp > d2.exp { - r := d.rescale(d2.exp) - r.value.Add(r.value, d2.getValue()) - return r - } + rd, rd2 := RescalePair(d, d2) - d3Value := new(big.Int).Add(d.getValue(), d2.getValue()) + d3Value := new(big.Int).Add(rd.value, rd2.value) return Decimal{ value: d3Value, - exp: d.exp, + exp: rd.exp, } } // Sub returns d - d2. func (d Decimal) Sub(d2 Decimal) Decimal { - // rescale returns a new value, so the difference can be stored in it - if d.exp < d2.exp { - r := d2.rescale(d.exp) - r.value.Sub(d.getValue(), r.value) - return r - } else if d.exp > d2.exp { - r := d.rescale(d2.exp) - r.value.Sub(r.value, d2.getValue()) - return r - } + rd, rd2 := RescalePair(d, d2) - d3Value := new(big.Int).Sub(d.getValue(), d2.getValue()) + d3Value := new(big.Int).Sub(rd.value, rd2.value) return Decimal{ value: d3Value, - exp: d.exp, + exp: rd.exp, } } // Neg returns -d. func (d Decimal) Neg() Decimal { - val := new(big.Int).Neg(d.getValue()) + d.ensureInitialized() + val := new(big.Int).Neg(d.value) return Decimal{ value: val, exp: d.exp, @@ -694,6 +452,9 @@ func (d Decimal) Neg() Decimal { // Mul returns d * d2. func (d Decimal) Mul(d2 Decimal) Decimal { + d.ensureInitialized() + d2.ensureInitialized() + expInt64 := int64(d.exp) + int64(d2.exp) if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 { // NOTE(vadim): better to panic than give incorrect results, as @@ -701,7 +462,7 @@ func (d Decimal) Mul(d2 Decimal) Decimal { panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64)) } - d3Value := new(big.Int).Mul(d.getValue(), d2.getValue()) + d3Value := new(big.Int).Mul(d.value, d2.value) return Decimal{ value: d3Value, exp: int32(expInt64), @@ -712,115 +473,60 @@ func (d Decimal) Mul(d2 Decimal) Decimal { // It shifts left when shift is positive and right if shift is negative. // In simpler terms, the given value for shift is added to the exponent // of the decimal. -// Shift panics if the resulting exponent does not fit in an int32. func (d Decimal) Shift(shift int32) Decimal { - exp := int64(d.exp) + int64(shift) - if exp > math.MaxInt32 || exp < math.MinInt32 { - panic(fmt.Sprintf("exponent %v overflows an int32!", exp)) - } - value := d.getValue() - if value.Sign() == 0 { - // a copy of zero has a nil word slice, which reflect.DeepEqual tells from an empty one - value = zeroInt - } + d.ensureInitialized() return Decimal{ - value: value, - exp: int32(exp), + value: new(big.Int).Set(d.value), + exp: d.exp + shift, } } -// Normalize returns d with the trailing zeros removed from its coefficient, so equal decimals get the -// same coefficient and exponent: 1.50, 1.5 and New(15, -1) all become New(15, -1), and every zero -// becomes Decimal{}, the same as Zero. It's like Java's BigDecimal.stripTrailingZeros. -// -// reflect.DeepEqual, and test helpers built on it such as testify's assert.Equal and gomock.Eq, compare -// the coefficient and exponent rather than the value, so normalize decimals before comparing them that -// way. Equal and Cmp compare values and don't need it. -// -// The value doesn't change, but its string form can: with TrimTrailingZeros = false, 1.50 prints as -// 1.5, and with UseScientificNotation = true, 1500 prints as 1.5E3. -func (d Decimal) Normalize() Decimal { - v := d.getValue() - if v.Sign() == 0 { - return Decimal{} - } - - // Count the trailing zeros with integer math: in v if it fits an int64, else in v mod 10^19, - // and in all the digits of v only when those 19 are all zeros. - var zeros int64 - if v.IsInt64() { - for x := v.Int64(); x%10 == 0; x /= 10 { - zeros++ - } - } else { - r := new(big.Int).Rem(v, pow10(19)) - for n := r.Abs(r).Uint64(); n%10 == 0 && zeros < 19; n /= 10 { - zeros++ - } - if zeros == 19 { - s := v.String() - zeros = int64(len(s) - len(strings.TrimRight(s, "0"))) - } - } - if room := math.MaxInt32 - int64(d.exp); zeros > room { - zeros = room - } - if zeros == 0 { - return d - } - return Decimal{value: new(big.Int).Quo(v, pow10(zeros)), exp: d.exp + int32(zeros)} -} - // Div returns d / d2. If it doesn't divide exactly, the result will have -// DivisionPrecision digits after the decimal point. Use DivRound to choose the precision per call. +// DivisionPrecision digits after the decimal point. func (d Decimal) Div(d2 Decimal) Decimal { return d.DivRound(d2, int32(DivisionPrecision)) } -// QuoRem does division with remainder +// QuoRem does divsion with remainder // d.QuoRem(d2,precision) returns quotient q and remainder r such that -// -// d = d2 * q + r, q an integer multiple of 10^(-precision) -// 0 <= r < abs(d2) * 10 ^(-precision) if d>=0 -// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0 -// +// d = d2 * q + r, q an integer multiple of 10^(-precision) +// 0 <= r < abs(d2) * 10 ^(-precision) if d>=0 +// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0 // Note that precision<0 is allowed as input. func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) { - return d.quoRem(d2, precision, nil) -} - -// quoRem is QuoRem that also sets bb, if not nil, to the divisor in the units of the remainder. -func (d Decimal) quoRem(d2 Decimal, precision int32, bb *big.Int) (Decimal, Decimal) { - if d2.getValue().Sign() == 0 { + d.ensureInitialized() + d2.ensureInitialized() + if d2.value.Sign() == 0 { panic("decimal division by 0") } scale := -precision - e := int64(d.exp) - int64(d2.exp) - int64(scale) - if precision == math.MinInt32 || e > math.MaxInt32 || e < math.MinInt32 { + e := int64(d.exp - d2.exp - scale) + if e > math.MaxInt32 || e < math.MinInt32 { panic("overflow in decimal QuoRem") } - if bb == nil { - bb = new(big.Int) - } - var aa big.Int + var aa, bb, expo big.Int var scalerest int32 // d = a 10^ea // d2 = b 10^eb if e < 0 { - aa = *d.getValue() - bb.Mul(d2.getValue(), pow10(-e)) + aa = *d.value + expo.SetInt64(-e) + bb.Exp(tenInt, &expo, nil) + bb.Mul(d2.value, &bb) scalerest = d.exp // now aa = a // bb = b 10^(scale + eb - ea) } else { - aa.Mul(d.getValue(), pow10(e)) - *bb = *d2.getValue() + expo.SetInt64(e) + aa.Exp(tenInt, &expo, nil) + aa.Mul(d.value, &aa) + bb = *d2.value scalerest = scale + d2.exp // now aa = a ^ (ea - eb - scale) // bb = b } var q, r big.Int - q.QuoRem(&aa, bb, &r) + q.QuoRem(&aa, &bb, &r) dq := Decimal{value: &q, exp: scale} dr := Decimal{value: &r, exp: scalerest} return dq, dr @@ -828,134 +534,72 @@ func (d Decimal) quoRem(d2 Decimal, precision int32, bb *big.Int) (Decimal, Deci // DivRound divides and rounds to a given precision // i.e. to an integer multiple of 10^(-precision) -// -// for a positive quotient digit 5 is rounded up, away from 0 -// if the quotient is negative then digit 5 is rounded down, away from 0 -// +// for a positive quotient digit 5 is rounded up, away from 0 +// if the quotient is negative then digit 5 is rounded down, away from 0 // Note that precision<0 is allowed as input. func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal { - // quoRem already checks initialization - var bb big.Int - q, r := d.quoRem(d2, precision, &bb) - // the actual rounding decision is based on comparing r*10^precision and d2/2, - // which is comparing 2*|r.value| and |bb| + // QuoRem already checks initialization + q, r := d.QuoRem(d2, precision) + // the actual rounding decision is based on comparing r*10^precision and d2/2 + // instead compare 2 r 10 ^precision and d2 var rv2 big.Int - rv2.Abs(r.getValue()) + rv2.Abs(r.value) rv2.Lsh(&rv2, 1) - if rv2.CmpAbs(&bb) < 0 { + // now rv2 = abs(r.value) * 2 + r2 := Decimal{value: &rv2, exp: r.exp + precision} + // r2 is now 2 * r * 10 ^ precision + var c = r2.Cmp(d2.Abs()) + + if c < 0 { return q } - if d.getValue().Sign()*d2.getValue().Sign() < 0 { - q.value.Sub(q.value, oneInt) - } else { - q.value.Add(q.value, oneInt) + if d.value.Sign()*d2.value.Sign() < 0 { + return q.Sub(New(1, -precision)) } - return q + + return q.Add(New(1, -precision)) } // Mod returns d % d2. func (d Decimal) Mod(d2 Decimal) Decimal { - _, r := d.QuoRem(d2, 0) - return r + quo := d.Div(d2).Truncate(0) + return d.Sub(d2.Mul(quo)) } -// NumDigits returns the number of digits of the decimal coefficient (d.Value) -func (d Decimal) NumDigits() int { - v := d.getValue() - if v.IsInt64() { - u := uint64(v.Int64()) - if u == 0 { - return 1 - } - if v.Sign() < 0 { - u = -u - } - // Not math.Log10, which rounds an exact 1eN like 1e15 down (see #420). - // bits.Len64(u)*1233>>12 is floor(log10(2^bits)), the digit count or one less. - n := bits.Len64(u) * 1233 >> 12 - if u >= pow10Uint64[n] { - n++ - } - return n +// Pow returns d to the power d2 +func (d Decimal) Pow(d2 Decimal) Decimal { + var temp Decimal + if d2.IntPart() == 0 { + return NewFromFloat(1) } - - estimatedNumDigits := int(float64(v.BitLen()) / math.Log2(10)) - - // estimatedNumDigits (lg10) may be off by 1, need to verify - if v.CmpAbs(pow10(int64(estimatedNumDigits))) >= 0 { - return estimatedNumDigits + 1 + temp = d.Pow(d2.Div(NewFromFloat(2))) + if d2.IntPart()%2 == 0 { + return temp.Mul(temp) } - - return estimatedNumDigits -} - -// IsInteger returns true when decimal can be represented as an integer value, otherwise, it returns false. -func (d Decimal) IsInteger() bool { - // The most typical case, all decimal with exponent higher or equal 0 can be represented as integer - if d.exp >= 0 { - return true + if d2.IntPart() > 0 { + return temp.Mul(temp).Mul(d) } - v := d.getValue() - if v.Sign() == 0 { - return true - } - if d.exp >= -18 && v.IsInt64() { - return v.Int64()%int64(pow10Uint64[-d.exp]) == 0 - } - // When the exponent is negative we have to check every number after the decimal place - // If all of them are zeroes, we are sure that given decimal can be represented as an integer - var r big.Int - q := new(big.Int).Set(v) - for z := abs(d.exp); z > 0; z-- { - q.QuoRem(q, tenInt, &r) - if r.Sign() != 0 { - return false - } - } - return true -} - -// Abs calculates absolute value of any int32. Used for calculating absolute value of decimal's exponent. -func abs(n int32) int32 { - if n < 0 { - return -n - } - return n + return temp.Mul(temp).Div(d) } // Cmp compares the numbers represented by d and d2 and returns: // -// -1 if d < d2 -// 0 if d == d2 -// +1 if d > d2 -func (d Decimal) Cmp(d2 Decimal) int { - if d.exp == d2.exp { - return d.getValue().Cmp(d2.getValue()) - } - if s, s2 := d.Sign(), d2.Sign(); s != s2 { - if s > s2 { - return 1 - } - return -1 - } else if s == 0 { - return 0 - } - - var scaled big.Int - if d.exp < d2.exp { - return d.getValue().Cmp(scaled.Mul(d2.getValue(), pow10(int64(d2.exp)-int64(d.exp)))) - } - return scaled.Mul(d.getValue(), pow10(int64(d.exp)-int64(d2.exp))).Cmp(d2.getValue()) -} - -// Compare compares the numbers represented by d and d2 and returns: +// -1 if d < d2 +// 0 if d == d2 +// +1 if d > d2 // -// -1 if d < d2 -// 0 if d == d2 -// +1 if d > d2 -func (d Decimal) Compare(d2 Decimal) int { - return d.Cmp(d2) +func (d Decimal) Cmp(d2 Decimal) int { + d.ensureInitialized() + d2.ensureInitialized() + + if d.exp == d2.exp { + return d.value.Cmp(d2.value) + } + + rd, rd2 := RescalePair(d, d2) + + return rd.value.Cmp(rd2.value) } // Equal returns whether the numbers represented by d and d2 are equal. @@ -963,9 +607,7 @@ func (d Decimal) Equal(d2 Decimal) bool { return d.Cmp(d2) == 0 } -// Equals returns whether the numbers represented by d and d2 are equal. -// -// Deprecated: Equals is deprecated, please use Equal method instead. +// Equals is deprecated, please use Equal method instead func (d Decimal) Equals(d2 Decimal) bool { return d.Equal(d2) } @@ -997,8 +639,12 @@ func (d Decimal) LessThanOrEqual(d2 Decimal) bool { // -1 if d < 0 // 0 if d == 0 // +1 if d > 0 +// func (d Decimal) Sign() int { - return d.getValue().Sign() + if d.value == nil { + return 0 + } + return d.value.Sign() } // IsPositive return @@ -1033,31 +679,26 @@ func (d Decimal) Exponent() int32 { return d.exp } -// Coefficient returns the coefficient of the decimal. It is scaled by 10^Exponent() +// Coefficient returns the coefficient of the decimal. It is scaled by 10^Exponent() func (d Decimal) Coefficient() *big.Int { - // we copy the coefficient so that mutating the result does not mutate the Decimal. - return new(big.Int).Set(d.getValue()) -} - -// CoefficientInt64 returns the coefficient of the decimal as int64. It is scaled by 10^Exponent() -// If coefficient cannot be represented in an int64, the result will be undefined. -func (d Decimal) CoefficientInt64() int64 { - return d.getValue().Int64() + d.ensureInitialized() + // we copy the coefficient so that mutating the result does not mutate the + // Decimal. + return big.NewInt(0).Set(d.value) } // IntPart returns the integer component of the decimal. func (d Decimal) IntPart() int64 { - if v := d.getValue(); d.exp <= 0 && d.exp >= -18 && v.IsInt64() { - return v.Int64() / int64(pow10Uint64[-d.exp]) - } scaledD := d.rescale(0) - return scaledD.getValue().Int64() + return scaledD.value.Int64() } // BigInt returns integer component of the decimal as a BigInt. func (d Decimal) BigInt() *big.Int { scaledD := d.rescale(0) - return scaledD.getValue() + i := &big.Int{} + i.SetString(scaledD.String(), 10) + return i } // BigFloat returns decimal as BigFloat. @@ -1070,12 +711,15 @@ func (d Decimal) BigFloat() *big.Float { // Rat returns a rational number representation of the decimal. func (d Decimal) Rat() *big.Rat { + d.ensureInitialized() if d.exp <= 0 { // NOTE(vadim): must negate after casting to prevent int32 overflow - return new(big.Rat).SetFrac(d.getValue(), pow10(-int64(d.exp))) + denom := new(big.Int).Exp(tenInt, big.NewInt(-int64(d.exp)), nil) + return new(big.Rat).SetFrac(d.value, denom) } - num := new(big.Int).Mul(d.getValue(), pow10(int64(d.exp))) + mul := new(big.Int).Exp(tenInt, big.NewInt(int64(d.exp)), nil) + num := new(big.Int).Mul(d.value, mul) return new(big.Rat).SetFrac(num, oneInt) } @@ -1083,53 +727,23 @@ func (d Decimal) Rat() *big.Rat { // whether f represents d exactly. // For more details, see the documentation for big.Rat.Float64 func (d Decimal) Float64() (f float64, exact bool) { - // Rat() materializes 10^|exp|, which takes forever for huge exponents. - // Values that far outside the float64 range don't need it: they always - // round to zero or overflow to infinity. - sign := d.Sign() - if sign == 0 { - return 0, true - } - // Both operands are exact here, so the division is correctly rounded like Rat().Float64(), - // and the result is exact when the 5^k part of 10^k divides the coefficient. - if v := d.getValue(); d.exp <= 0 && d.exp >= -22 && v.IsInt64() { - if i := v.Int64(); i >= -1<<53 && i <= 1<<53 { - return float64(i) / float64Pow10[-d.exp], i%pow5Int64[-d.exp] == 0 - } - } - // |d| lies in [10^(magnitude-1), 10^magnitude). float64 spans roughly - // 1e-324..1e308, so ±400 is safely outside it with margin to spare. - magnitude := int64(d.NumDigits()) + int64(d.exp) - if magnitude < -400 { - return math.Copysign(0, float64(sign)), false - } - if magnitude > 400 { - return math.Inf(sign), false - } - return d.Rat().Float64() } -// InexactFloat64 returns the nearest float64 value for d. -// It doesn't indicate if the returned value represents d exactly. -func (d Decimal) InexactFloat64() float64 { - f, _ := d.Float64() - return f -} - // String returns the string representation of the decimal // with the fixed point. // // Example: // -// d := New(-12345, -3) -// println(d.String()) +// d := New(-12345, -3) +// println(d.String()) // // Output: // -// -12.345 +// -12.345 +// func (d Decimal) String() string { - return d.string(TrimTrailingZeros, UseScientificNotation) + return d.string(true) } // StringFixed returns a rounded fixed-point string with places digits after @@ -1137,18 +751,17 @@ func (d Decimal) String() string { // // Example: // -// NewFromFloat(0).StringFixed(2) // output: "0.00" -// NewFromFloat(0).StringFixed(0) // output: "0" -// NewFromFloat(5.45).StringFixed(0) // output: "5" -// NewFromFloat(5.45).StringFixed(1) // output: "5.5" -// NewFromFloat(5.45).StringFixed(2) // output: "5.45" -// NewFromFloat(5.45).StringFixed(3) // output: "5.450" -// NewFromFloat(545).StringFixed(-1) // output: "550" +// NewFromFloat(0).StringFixed(2) // output: "0.00" +// NewFromFloat(0).StringFixed(0) // output: "0" +// NewFromFloat(5.45).StringFixed(0) // output: "5" +// NewFromFloat(5.45).StringFixed(1) // output: "5.5" +// NewFromFloat(5.45).StringFixed(2) // output: "5.45" +// NewFromFloat(5.45).StringFixed(3) // output: "5.450" +// NewFromFloat(545).StringFixed(-1) // output: "550" // -// Regardless of the UseScientificNotation option, the returned string will never be in scientific notation. func (d Decimal) StringFixed(places int32) string { rounded := d.Round(places) - return rounded.string(false, false) + return rounded.string(false) } // StringFixedBank returns a banker rounded fixed-point string with places digits @@ -1156,127 +769,24 @@ func (d Decimal) StringFixed(places int32) string { // // Example: // -// NewFromFloat(0).StringFixedBank(2) // output: "0.00" -// NewFromFloat(0).StringFixedBank(0) // output: "0" -// NewFromFloat(5.45).StringFixedBank(0) // output: "5" -// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4" -// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45" -// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450" -// NewFromFloat(545).StringFixedBank(-1) // output: "540" +// NewFromFloat(0).StringFixedBank(2) // output: "0.00" +// NewFromFloat(0).StringFixedBank(0) // output: "0" +// NewFromFloat(5.45).StringFixedBank(0) // output: "5" +// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4" +// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45" +// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450" +// NewFromFloat(545).StringFixedBank(-1) // output: "540" // -// Regardless of the UseScientificNotation option, the returned string will never be in scientific notation. func (d Decimal) StringFixedBank(places int32) string { rounded := d.RoundBank(places) - return rounded.string(false, false) + return rounded.string(false) } // StringFixedCash returns a Swedish/Cash rounded fixed-point string. For // more details see the documentation at function RoundCash. -// -// Regardless of the UseScientificNotation option, the returned string will never be in scientific notation. func (d Decimal) StringFixedCash(interval uint8) string { rounded := d.RoundCash(interval) - return rounded.string(false, false) -} - -// Format implements fmt.Formatter, so decimals work with the fmt verbs: -// -// - %v and %s print String(), and %q, %x and %X quote it or print it in hex, as for any fmt.Stringer. -// - %f and %F print fixed-point notation. With a precision, as in %.2f, the decimal is rounded like -// StringFixed. Without one, it's printed exactly, like String() but never in scientific notation. -// Width and the '-', '+', ' ' and '0' flags work as they do for floats. -// - %#v prints Go syntax that recreates the decimal exactly, such as decimal.New(314, -2). -// -// Other verbs print fmt's usual error, such as %!d(decimal.Decimal=3.14). -func (d Decimal) Format(f fmt.State, verb rune) { - switch verb { - case 'v', 's', 'q', 'x', 'X': - if verb == 'v' && f.Flag('#') { - formatString(f, 's', d.goSyntax()) - } else { - formatString(f, verb, d.String()) - } - case 'f', 'F': - d.formatFixed(f) - default: - _, _ = fmt.Fprintf(f, "%%!%c(decimal.Decimal=%s)", verb, d.String()) - } -} - -// formatString formats s as fmt would for the directive that called Format, with verb in place of its own. -func formatString(f fmt.State, verb rune, s string) { - // Println and plain %v are the common case, and rebuilding the directive costs 3 allocations. - _, hasWidth := f.Width() - _, hasPrec := f.Precision() - if (verb == 'v' || verb == 's') && !hasWidth && !hasPrec { - _, _ = io.WriteString(f, s) - return - } - _, _ = fmt.Fprintf(f, fmtDirective(f, verb), s) -} - -// fmtDirective rebuilds the directive that called Format, as fmt.FormatString does from Go 1.20. -func fmtDirective(f fmt.State, verb rune) string { - b := []byte{'%'} - for _, flag := range "+-# 0" { - if f.Flag(int(flag)) { - b = append(b, byte(flag)) - } - } - if width, ok := f.Width(); ok { - b = strconv.AppendInt(b, int64(width), 10) - } - if prec, ok := f.Precision(); ok { - b = strconv.AppendInt(append(b, '.'), int64(prec), 10) - } - return string(append(b, string(verb)...)) -} - -func (d Decimal) formatFixed(f fmt.State) { - var s string - if prec, ok := f.Precision(); ok { - s = d.StringFixed(int32(prec)) - } else { - s = d.string(TrimTrailingZeros, false) - } - - sign := "" - switch { - case s[0] == '-': - sign, s = "-", s[1:] - case f.Flag('+'): - sign = "+" - case f.Flag(' '): - sign = " " - } - // Before Go 1.23, fmt keeps the width of an earlier directive when ok is false. - pad := 0 - if width, ok := f.Width(); ok { - pad = width - len(sign) - len(s) - } - switch { - case pad <= 0: - s = sign + s - case f.Flag('-'): - s = sign + s + strings.Repeat(" ", pad) - case f.Flag('0'): - s = sign + strings.Repeat("0", pad) + s - default: - s = strings.Repeat(" ", pad) + sign + s - } - _, _ = io.WriteString(f, s) -} - -func (d Decimal) goSyntax() string { - switch { - case d.value == nil: - return "decimal.Decimal{}" - case d.value.IsInt64(): - return fmt.Sprintf("decimal.New(%d, %d)", d.value.Int64(), d.exp) - default: - // Shift instead of an exponent in the string, which RequireFromString caps at MaxDecodeExponent. - return fmt.Sprintf("decimal.RequireFromString(%q).Shift(%d)", d.value.String(), d.exp) - } + return rounded.string(false) } // Round rounds the decimal to places decimal places. @@ -1284,12 +794,10 @@ func (d Decimal) goSyntax() string { // // Example: // -// NewFromFloat(5.45).Round(1).String() // output: "5.5" -// NewFromFloat(545).Round(-1).String() // output: "550" (with UseScientificNotation false, "5.5E2" if true) +// NewFromFloat(5.45).Round(1).String() // output: "5.5" +// NewFromFloat(545).Round(-1).String() // output: "550" +// func (d Decimal) Round(places int32) Decimal { - if d.exp == -places { - return d - } // truncate to places + 1 ret := d.rescale(-places - 1) @@ -1300,107 +808,16 @@ func (d Decimal) Round(places int32) Decimal { ret.value.Add(ret.value, fiveInt) } - // truncate towards zero - ret.value.Quo(ret.value, tenInt) + // floor for positive numbers, ceil for negative numbers + _, m := ret.value.DivMod(ret.value, tenInt, new(big.Int)) ret.exp++ + if ret.value.Sign() < 0 && m.Cmp(zeroInt) != 0 { + ret.value.Add(ret.value, oneInt) + } return ret } -// RoundCeil rounds the decimal towards +infinity. -// -// Example: -// -// NewFromFloat(545).RoundCeil(-2).String() // output: "600" -// NewFromFloat(500).RoundCeil(-2).String() // output: "500" -// NewFromFloat(1.1001).RoundCeil(2).String() // output: "1.11" -// NewFromFloat(-1.454).RoundCeil(1).String() // output: "-1.4" -func (d Decimal) RoundCeil(places int32) Decimal { - if d.exp >= -places { - return d.rescale(-places) - } - - // q is d truncated to places, r is not 0 when digits were dropped and has the sign of d - var r big.Int - q := new(big.Int) - q.QuoRem(d.getValue(), pow10(-int64(places)-int64(d.exp)), &r) - if r.Sign() > 0 { - q.Add(q, oneInt) - } else if q.Sign() == 0 && r.Sign() != 0 { - // reflect.DeepEqual tells an empty word slice from nil, keep the empty one that - // rounding a nonzero value to zero used to leave - q.SetBits([]big.Word{}) - } - - return Decimal{value: q, exp: -places} -} - -// RoundFloor rounds the decimal towards -infinity. -// -// Example: -// -// NewFromFloat(545).RoundFloor(-2).String() // output: "500" -// NewFromFloat(-500).RoundFloor(-2).String() // output: "-500" -// NewFromFloat(1.1001).RoundFloor(2).String() // output: "1.1" -// NewFromFloat(-1.454).RoundFloor(1).String() // output: "-1.5" -func (d Decimal) RoundFloor(places int32) Decimal { - if d.exp >= -places { - return d.rescale(-places) - } - - // q is d truncated to places, r is not 0 when digits were dropped and has the sign of d - var r big.Int - q := new(big.Int) - q.QuoRem(d.getValue(), pow10(-int64(places)-int64(d.exp)), &r) - if r.Sign() < 0 { - q.Sub(q, oneInt) - } else if q.Sign() == 0 && r.Sign() != 0 { - // reflect.DeepEqual tells an empty word slice from nil, keep the empty one that - // rounding a nonzero value to zero used to leave - q.SetBits([]big.Word{}) - } - - return Decimal{value: q, exp: -places} -} - -// RoundUp rounds the decimal away from zero. -// -// Example: -// -// NewFromFloat(545).RoundUp(-2).String() // output: "600" -// NewFromFloat(500).RoundUp(-2).String() // output: "500" -// NewFromFloat(1.1001).RoundUp(2).String() // output: "1.11" -// NewFromFloat(-1.454).RoundUp(1).String() // output: "-1.5" -func (d Decimal) RoundUp(places int32) Decimal { - if d.exp >= -places { - return d.rescale(-places) - } - - // q is d truncated to places, r is not 0 when digits were dropped and has the sign of d - var r big.Int - q := new(big.Int) - q.QuoRem(d.getValue(), pow10(-int64(places)-int64(d.exp)), &r) - if r.Sign() > 0 { - q.Add(q, oneInt) - } else if r.Sign() < 0 { - q.Sub(q, oneInt) - } - - return Decimal{value: q, exp: -places} -} - -// RoundDown rounds the decimal towards zero. -// -// Example: -// -// NewFromFloat(545).RoundDown(-2).String() // output: "500" -// NewFromFloat(-500).RoundDown(-2).String() // output: "-500" -// NewFromFloat(1.1001).RoundDown(2).String() // output: "1.1" -// NewFromFloat(-1.454).RoundDown(1).String() // output: "-1.4" -func (d Decimal) RoundDown(places int32) Decimal { - return d.rescale(-places) -} - // RoundBank rounds the decimal to places decimal places. // If the final digit to round is equidistant from the nearest two integers the // rounded value is taken as the even number @@ -1409,26 +826,24 @@ func (d Decimal) RoundDown(places int32) Decimal { // // Examples: // -// NewFromFloat(5.45).RoundBank(1).String() // output: "5.4" -// NewFromFloat(545).RoundBank(-1).String() // output: "540" -// NewFromFloat(5.46).RoundBank(1).String() // output: "5.5" -// NewFromFloat(546).RoundBank(-1).String() // output: "550" -// NewFromFloat(5.55).RoundBank(1).String() // output: "5.6" -// NewFromFloat(555).RoundBank(-1).String() // output: "560" +// NewFromFloat(5.45).Round(1).String() // output: "5.4" +// NewFromFloat(545).Round(-1).String() // output: "540" +// NewFromFloat(5.46).Round(1).String() // output: "5.5" +// NewFromFloat(546).Round(-1).String() // output: "550" +// NewFromFloat(5.55).Round(1).String() // output: "5.6" +// NewFromFloat(555).Round(-1).String() // output: "560" +// func (d Decimal) RoundBank(places int32) Decimal { round := d.Round(places) + remainder := d.Sub(round).Abs() - // it is a tie when twice the k dropped digits equal 10^k - if k := -int64(places) - int64(d.exp); k > 0 && round.getValue().Bit(0) != 0 { - var dropped big.Int - dropped.Rem(d.getValue(), pow10(k)) - if dropped.Abs(&dropped).Lsh(&dropped, 1).Cmp(pow10(k)) == 0 { - if round.getValue().Sign() < 0 { - round.value = new(big.Int).Add(round.getValue(), oneInt) - } else { - round.value = new(big.Int).Sub(round.getValue(), oneInt) - } + half := New(5, -places-1) + if remainder.Cmp(half) == 0 && round.value.Bit(0) != 0 { + if round.value.Sign() < 0 { + round.value.Add(round.value, oneInt) + } else { + round.value.Sub(round.value, oneInt) } } @@ -1439,13 +854,11 @@ func (d Decimal) RoundBank(places int32) Decimal { // interval. The amount payable for a cash transaction is rounded to the nearest // multiple of the minimum currency unit available. The following intervals are // available: 5, 10, 25, 50 and 100; any other number throws a panic. -// -// 5: 5 cent rounding 3.43 => 3.45 -// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up) -// 25: 25 cent rounding 3.41 => 3.50 -// 50: 50 cent rounding 3.75 => 4.00 -// 100: 100 cent rounding 3.50 => 4.00 -// +// 5: 5 cent rounding 3.43 => 3.45 +// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up) +// 25: 25 cent rounding 3.41 => 3.50 +// 50: 50 cent rounding 3.75 => 4.00 +// 100: 100 cent rounding 3.50 => 4.00 // For more details: https://en.wikipedia.org/wiki/Cash_rounding func (d Decimal) RoundCash(interval uint8) Decimal { var iVal *big.Int @@ -1467,29 +880,42 @@ func (d Decimal) RoundCash(interval uint8) Decimal { value: iVal, } - return d.Mul(dVal).Round(0).DivRound(dVal, 2) + // TODO: optimize those calculations to reduce the high allocations (~29 allocs). + return d.Mul(dVal).Round(0).Div(dVal).Truncate(2) } // Floor returns the nearest integer value less than or equal to d. func (d Decimal) Floor() Decimal { + d.ensureInitialized() + if d.exp >= 0 { return d } + exp := big.NewInt(10) + // NOTE(vadim): must negate after casting to prevent int32 overflow - z := new(big.Int).Div(d.getValue(), pow10(-int64(d.exp))) + exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) + + z := new(big.Int).Div(d.value, exp) return Decimal{value: z, exp: 0} } // Ceil returns the nearest integer value greater than or equal to d. func (d Decimal) Ceil() Decimal { + d.ensureInitialized() + if d.exp >= 0 { return d } + exp := big.NewInt(10) + // NOTE(vadim): must negate after casting to prevent int32 overflow - z, m := new(big.Int).DivMod(d.getValue(), pow10(-int64(d.exp)), new(big.Int)) - if m.Sign() != 0 { + exp.Exp(exp, big.NewInt(-int64(d.exp)), nil) + + z, m := new(big.Int).DivMod(d.value, exp, new(big.Int)) + if m.Cmp(zeroInt) != 0 { z.Add(z, oneInt) } return Decimal{value: z, exp: 0} @@ -1497,47 +923,159 @@ func (d Decimal) Ceil() Decimal { // Truncate truncates off digits from the number, without rounding. // -// If precision >= 0, it specifies the number of decimal places to keep. -// If precision < 0, it truncates the integer part to the nearest 10^(-precision) -// towards zero. +// NOTE: precision is the last digit that will not be truncated (must be >= 0). // // Example: // -// decimal.RequireFromString("123.456").Truncate(2).String() // output: "123.45" -// decimal.RequireFromString("5432").Truncate(-2).String() // output: "5400" -// decimal.RequireFromString("-5432").Truncate(-2).String() // output: "-5400" +// decimal.NewFromString("123.456").Truncate(2).String() // "123.45" +// func (d Decimal) Truncate(precision int32) Decimal { - if -precision > d.exp { + d.ensureInitialized() + if precision >= 0 && -precision > d.exp { return d.rescale(-precision) } return d } +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error { + if string(decimalBytes) == "null" { + return nil + } + + str, err := unquoteIfQuoted(decimalBytes) + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", decimalBytes, err) + } + + decimal, err := NewFromString(str) + *d = decimal + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", str, err) + } + return nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (d Decimal) MarshalJSON() ([]byte, error) { + var str string + if MarshalJSONWithoutQuotes { + str = d.String() + } else { + str = "\"" + d.String() + "\"" + } + return []byte(str), nil +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation +// is already used when encoding to text, this method stores that string as []byte +func (d *Decimal) UnmarshalBinary(data []byte) error { + // Extract the exponent + d.exp = int32(binary.BigEndian.Uint32(data[:4])) + + // Extract the value + d.value = new(big.Int) + return d.value.GobDecode(data[4:]) +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (d Decimal) MarshalBinary() (data []byte, err error) { + // Write the exponent first since it's a fixed size + v1 := make([]byte, 4) + binary.BigEndian.PutUint32(v1, uint32(d.exp)) + + // Add the value + var v2 []byte + if v2, err = d.value.GobEncode(); err != nil { + return + } + + // Return the byte array + data = append(v1, v2...) + return +} + +// Scan implements the sql.Scanner interface for database deserialization. +func (d *Decimal) Scan(value interface{}) error { + // first try to see if the data is stored in database as a Numeric datatype + switch v := value.(type) { + + case float32: + *d = NewFromFloat(float64(v)) + return nil + + case float64: + // numeric in sqlite3 sends us float64 + *d = NewFromFloat(v) + return nil + + case int64: + // at least in sqlite3 when the value is 0 in db, the data is sent + // to us as an int64 instead of a float64 ... + *d = New(v, 0) + return nil + + default: + // default is trying to interpret value stored as string + str, err := unquoteIfQuoted(v) + if err != nil { + return err + } + *d, err = NewFromString(str) + return err + } +} + +// Value implements the driver.Valuer interface for database serialization. +func (d Decimal) Value() (driver.Value, error) { + return d.String(), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for XML +// deserialization. +func (d *Decimal) UnmarshalText(text []byte) error { + str := string(text) + + dec, err := NewFromString(str) + *d = dec + if err != nil { + return fmt.Errorf("error decoding string '%s': %s", str, err) + } + + return nil +} + +// MarshalText implements the encoding.TextMarshaler interface for XML +// serialization. +func (d Decimal) MarshalText() (text []byte, err error) { + return []byte(d.String()), nil +} + +// GobEncode implements the gob.GobEncoder interface for gob serialization. +func (d Decimal) GobEncode() ([]byte, error) { + return d.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface for gob serialization. +func (d *Decimal) GobDecode(data []byte) error { + return d.UnmarshalBinary(data) +} + // StringScaled first scales the decimal then calls .String() on it. -// -// Deprecated: buggy and unintuitive. Use StringFixed instead. +// NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead. func (d Decimal) StringScaled(exp int32) string { return d.rescale(exp).String() } -func (d Decimal) string(trimTrailingZeros, useScientificNotation bool) string { - if d.exp == 0 { - return d.getValue().String() - } +func (d Decimal) string(trimTrailingZeros bool) string { if d.exp >= 0 { - if useScientificNotation { - return d.ScientificNotationString() - } return d.rescale(0).value.String() } - str := d.getValue().String() - sign := "" - if str[0] == '-' { - sign, str = "-", str[1:] - } + abs := new(big.Int).Abs(d.value) + str := abs.String() - var intPart, leadingZeros, fractionalPart string + var intPart, fractionalPart string // NOTE(vadim): this cast to int will cause bugs if d.exp == INT_MIN // and you are on a 32-bit machine. Won't fix this super-edge case. @@ -1549,12 +1087,7 @@ func (d Decimal) string(trimTrailingZeros, useScientificNotation bool) string { intPart = "0" num0s := -dExpInt - len(str) - if num0s <= len(zeros) { - leadingZeros = zeros[:num0s] - } else { - leadingZeros = strings.Repeat("0", num0s) - } - fractionalPart = str + fractionalPart = strings.Repeat("0", num0s) + str } if trimTrailingZeros { @@ -1565,47 +1098,31 @@ func (d Decimal) string(trimTrailingZeros, useScientificNotation bool) string { } } fractionalPart = fractionalPart[:i+1] - if fractionalPart == "" { - leadingZeros = "" - } } - if len(leadingZeros)+len(fractionalPart) > 0 { - return sign + intPart + "." + leadingZeros + fractionalPart + number := intPart + if len(fractionalPart) > 0 { + number += "." + fractionalPart } - return sign + intPart -} -// ScientificNotationString serializes the decimal into standard scientific notation. -// -// The notation is normalized to have one non-zero digit followed by a decimal point and -// the remaining significant digits followed by "E" and the base-10 exponent. -// -// A zero, which has no significant digits, is simply serialized to "0". -func (d Decimal) ScientificNotationString() string { - exp := int(d.exp) - intStr := new(big.Int).Abs(d.getValue()).String() - if intStr == "0" { - return intStr - } - first := intStr[0] - var remaining string - if len(intStr) > 1 { - remaining = "." + intStr[1:] - exp = exp + len(intStr) - 1 - } - number := string(first) + remaining + "E" + strconv.Itoa(exp) if d.value.Sign() < 0 { return "-" + number } + return number } +func (d *Decimal) ensureInitialized() { + if d.value == nil { + d.value = new(big.Int) + } +} + // Min returns the smallest Decimal that was passed in the arguments. // // To call this function with an array, you must do: // -// Min(arr[0], arr[1:]...) +// Min(arr[0], arr[1:]...) // // This makes it harder to accidentally call Min with 0 arguments. func Min(first Decimal, rest ...Decimal) Decimal { @@ -1622,7 +1139,7 @@ func Min(first Decimal, rest ...Decimal) Decimal { // // To call this function with an array, you must do: // -// Max(arr[0], arr[1:]...) +// Max(arr[0], arr[1:]...) // // This makes it harder to accidentally call Max with 0 arguments. func Max(first Decimal, rest ...Decimal) Decimal { @@ -1637,31 +1154,12 @@ func Max(first Decimal, rest ...Decimal) Decimal { // Sum returns the combined total of the provided first and rest Decimals func Sum(first Decimal, rest ...Decimal) Decimal { - if len(rest) == 0 { - return first + total := first + for _, item := range rest { + total = total.Add(item) } - // Add returns a new value, so the following items can be added to it in place - total := first.Add(rest[0]) - last := len(rest) - 1 - if last == 0 { - return total - } - var scaled big.Int - for _, item := range rest[1:last] { - switch { - case item.exp < total.exp: - total = total.rescale(item.exp) - total.value.Add(total.value, item.getValue()) - case item.exp > total.exp: - total.value.Add(total.value, scaled.Mul(item.getValue(), pow10(int64(item.exp)-int64(total.exp)))) - default: - total.value.Add(total.value, item.getValue()) - } - } - - // the last Add builds the result the same way as adding one item at a time does - return total.Add(rest[last]) + return total } // Avg returns the average value of the provided first and rest Decimals @@ -1673,11 +1171,307 @@ func Avg(first Decimal, rest ...Decimal) Decimal { // RescalePair rescales two decimals to common exponential value (minimal exp of both decimals) func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) { - if d1.exp < d2.exp { - return d1, d2.rescale(d1.exp) - } else if d1.exp > d2.exp { - return d1.rescale(d2.exp), d2 + d1.ensureInitialized() + d2.ensureInitialized() + + if d1.exp == d2.exp { + return d1, d2 } - return d1, d2 + baseScale := min(d1.exp, d2.exp) + if baseScale != d1.exp { + return d1.rescale(baseScale), d2 + } + return d1, d2.rescale(baseScale) +} + +func min(x, y int32) int32 { + if x >= y { + return y + } + return x +} + +func unquoteIfQuoted(value interface{}) (string, error) { + var bytes []byte + + switch v := value.(type) { + case string: + bytes = []byte(v) + case []byte: + bytes = v + default: + return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'", + value, value) + } + + // If the amount is quoted, strip the quotes + if len(bytes) > 2 && bytes[0] == '"' && bytes[len(bytes)-1] == '"' { + bytes = bytes[1 : len(bytes)-1] + } + return string(bytes), nil +} + +// NullDecimal represents a nullable decimal with compatibility for +// scanning null values from the database. +type NullDecimal struct { + Decimal Decimal + Valid bool +} + +// Scan implements the sql.Scanner interface for database deserialization. +func (d *NullDecimal) Scan(value interface{}) error { + if value == nil { + d.Valid = false + return nil + } + d.Valid = true + return d.Decimal.Scan(value) +} + +// Value implements the driver.Valuer interface for database serialization. +func (d NullDecimal) Value() (driver.Value, error) { + if !d.Valid { + return nil, nil + } + return d.Decimal.Value() +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error { + if string(decimalBytes) == "null" { + d.Valid = false + return nil + } + d.Valid = true + return d.Decimal.UnmarshalJSON(decimalBytes) +} + +// MarshalJSON implements the json.Marshaler interface. +func (d NullDecimal) MarshalJSON() ([]byte, error) { + if !d.Valid { + return []byte("null"), nil + } + return d.Decimal.MarshalJSON() +} + +// Trig functions + +// Atan returns the arctangent, in radians, of x. +func (d Decimal) Atan() Decimal { + if d.Equal(NewFromFloat(0.0)) { + return d + } + if d.GreaterThan(NewFromFloat(0.0)) { + return d.satan() + } + return d.Neg().satan().Neg() +} + +func (d Decimal) xatan() Decimal { + P0 := NewFromFloat(-8.750608600031904122785e-01) + P1 := NewFromFloat(-1.615753718733365076637e+01) + P2 := NewFromFloat(-7.500855792314704667340e+01) + P3 := NewFromFloat(-1.228866684490136173410e+02) + P4 := NewFromFloat(-6.485021904942025371773e+01) + Q0 := NewFromFloat(2.485846490142306297962e+01) + Q1 := NewFromFloat(1.650270098316988542046e+02) + Q2 := NewFromFloat(4.328810604912902668951e+02) + Q3 := NewFromFloat(4.853903996359136964868e+02) + Q4 := NewFromFloat(1.945506571482613964425e+02) + z := d.Mul(d) + b1 := P0.Mul(z).Add(P1).Mul(z).Add(P2).Mul(z).Add(P3).Mul(z).Add(P4).Mul(z) + b2 := z.Add(Q0).Mul(z).Add(Q1).Mul(z).Add(Q2).Mul(z).Add(Q3).Mul(z).Add(Q4) + z = b1.Div(b2) + z = d.Mul(z).Add(d) + return z +} + +// satan reduces its argument (known to be positive) +// to the range [0, 0.66] and calls xatan. +func (d Decimal) satan() Decimal { + Morebits := NewFromFloat(6.123233995736765886130e-17) // pi/2 = PIO2 + Morebits + Tan3pio8 := NewFromFloat(2.41421356237309504880) // tan(3*pi/8) + pi := NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459) + + if d.LessThanOrEqual(NewFromFloat(0.66)) { + return d.xatan() + } + if d.GreaterThan(Tan3pio8) { + return pi.Div(NewFromFloat(2.0)).Sub(NewFromFloat(1.0).Div(d).xatan()).Add(Morebits) + } + return pi.Div(NewFromFloat(4.0)).Add((d.Sub(NewFromFloat(1.0)).Div(d.Add(NewFromFloat(1.0)))).xatan()).Add(NewFromFloat(0.5).Mul(Morebits)) +} + +// sin coefficients +var _sin = [...]Decimal{ + NewFromFloat(1.58962301576546568060e-10), // 0x3de5d8fd1fd19ccd + NewFromFloat(-2.50507477628578072866e-8), // 0xbe5ae5e5a9291f5d + NewFromFloat(2.75573136213857245213e-6), // 0x3ec71de3567d48a1 + NewFromFloat(-1.98412698295895385996e-4), // 0xbf2a01a019bfdf03 + NewFromFloat(8.33333333332211858878e-3), // 0x3f8111111110f7d0 + NewFromFloat(-1.66666666666666307295e-1), // 0xbfc5555555555548 +} + +// Sin returns the sine of the radian argument x. +func (d Decimal) Sin() Decimal { + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + if d.Equal(NewFromFloat(0.0)) { + return d + } + // make argument positive but save the sign + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + sign = true + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + // reflect in x axis + if j > 3 { + sign = !sign + j -= 4 + } + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if j == 1 || j == 2 { + w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) + y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w) + } else { + y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) + } + if sign { + y = y.Neg() + } + return y +} + +// cos coefficients +var _cos = [...]Decimal{ + NewFromFloat(-1.13585365213876817300e-11), // 0xbda8fa49a0861a9b + NewFromFloat(2.08757008419747316778e-9), // 0x3e21ee9d7b4e3f05 + NewFromFloat(-2.75573141792967388112e-7), // 0xbe927e4f7eac4bc6 + NewFromFloat(2.48015872888517045348e-5), // 0x3efa01a019c844f5 + NewFromFloat(-1.38888888888730564116e-3), // 0xbf56c16c16c14f91 + NewFromFloat(4.16666666666665929218e-2), // 0x3fa555555555554b +} + +// Cos returns the cosine of the radian argument x. +func (d Decimal) Cos() Decimal { + + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + // make argument positive + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + j &= 7 // octant modulo 2Pi radians (360 degrees) + // reflect in x axis + if j > 3 { + sign = !sign + j -= 4 + } + if j > 1 { + sign = !sign + } + + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if j == 1 || j == 2 { + y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) + } else { + w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) + y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w) + } + if sign { + y = y.Neg() + } + return y +} + +var _tanP = [...]Decimal{ + NewFromFloat(-1.30936939181383777646e+4), // 0xc0c992d8d24f3f38 + NewFromFloat(1.15351664838587416140e+6), // 0x413199eca5fc9ddd + NewFromFloat(-1.79565251976484877988e+7), // 0xc1711fead3299176 +} +var _tanQ = [...]Decimal{ + NewFromFloat(1.00000000000000000000e+0), + NewFromFloat(1.36812963470692954678e+4), //0x40cab8a5eeb36572 + NewFromFloat(-1.32089234440210967447e+6), //0xc13427bc582abc96 + NewFromFloat(2.50083801823357915839e+7), //0x4177d98fc2ead8ef + NewFromFloat(-5.38695755929454629881e+7), //0xc189afe03cbe5a31 +} + +// Tan returns the tangent of the radian argument x. +func (d Decimal) Tan() Decimal { + + PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts + PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000, + PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170, + M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi + + if d.Equal(NewFromFloat(0.0)) { + return d + } + + // make argument positive but save the sign + sign := false + if d.LessThan(NewFromFloat(0.0)) { + d = d.Neg() + sign = true + } + + j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle + y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float + + // map zeros to origin + if j&1 == 1 { + j++ + y = y.Add(NewFromFloat(1.0)) + } + + z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic + zz := z.Mul(z) + + if zz.GreaterThan(NewFromFloat(1e-14)) { + w := zz.Mul(_tanP[0].Mul(zz).Add(_tanP[1]).Mul(zz).Add(_tanP[2])) + x := zz.Add(_tanQ[1]).Mul(zz).Add(_tanQ[2]).Mul(zz).Add(_tanQ[3]).Mul(zz).Add(_tanQ[4]) + y = z.Add(z.Mul(w.Div(x))) + } else { + y = z + } + if j&2 == 2 { + y = NewFromFloat(-1.0).Div(y) + } + if sign { + y = y.Neg() + } + return y } diff --git a/decimal_bench_test.go b/decimal_bench_test.go index 06f0bb9..d77c275 100644 --- a/decimal_bench_test.go +++ b/decimal_bench_test.go @@ -1,9 +1,7 @@ package decimal import ( - "fmt" "math" - "math/big" "math/rand" "sort" "strconv" @@ -23,9 +21,10 @@ func BenchmarkNewFromFloatWithExponent(b *testing.B) { in[i] = rng.NormFloat64() * 10e20 } b.ReportAllocs() - b.ResetTimer() + b.StartTimer() for i := 0; i < b.N; i++ { - _ = NewFromFloatWithExponent(in[i], math.MinInt32) + in := rng.NormFloat64() * 10e20 + _ = NewFromFloatWithExponent(in, math.MinInt32) } } @@ -36,7 +35,7 @@ func BenchmarkNewFromFloat(b *testing.B) { in[i] = rng.NormFloat64() * 10e20 } b.ReportAllocs() - b.ResetTimer() + b.StartTimer() for i := 0; i < b.N; i++ { _ = NewFromFloat(in[i]) } @@ -44,14 +43,15 @@ func BenchmarkNewFromFloat(b *testing.B) { func BenchmarkNewFromStringFloat(b *testing.B) { rng := rand.New(rand.NewSource(0xdead1337)) - in := make([]string, b.N) + in := make([]float64, b.N) for i := range in { - in[i] = strconv.FormatFloat(rng.NormFloat64()*10e20, 'f', -1, 64) + in[i] = rng.NormFloat64() * 10e20 } b.ReportAllocs() - b.ResetTimer() + b.StartTimer() for i := 0; i < b.N; i++ { - _, _ = NewFromString(in[i]) + in := strconv.FormatFloat(in[i], 'f', -1, 64) + _, _ = NewFromString(in) } } @@ -119,34 +119,6 @@ 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) { decimals := DecimalSlice([]Decimal{}) for i := 0; i < 1000000; i++ { @@ -158,7 +130,7 @@ func Benchmark_Cmp(b *testing.B) { } } -func BenchmarkDecimal_Add_different_precision(b *testing.B) { +func Benchmark_decimal_Decimal_Add_different_precision(b *testing.B) { d1 := NewFromFloat(1000.123) d2 := NewFromFloat(500).Mul(NewFromFloat(0.12)) @@ -169,7 +141,7 @@ func BenchmarkDecimal_Add_different_precision(b *testing.B) { } } -func BenchmarkDecimal_Sub_different_precision(b *testing.B) { +func Benchmark_decimal_Decimal_Sub_different_precision(b *testing.B) { d1 := NewFromFloat(1000.123) d2 := NewFromFloat(500).Mul(NewFromFloat(0.12)) @@ -180,7 +152,7 @@ func BenchmarkDecimal_Sub_different_precision(b *testing.B) { } } -func BenchmarkDecimal_Add_same_precision(b *testing.B) { +func Benchmark_decimal_Decimal_Add_same_precision(b *testing.B) { d1 := NewFromFloat(1000.123) d2 := NewFromFloat(500.123) @@ -191,183 +163,13 @@ func BenchmarkDecimal_Add_same_precision(b *testing.B) { } } -func BenchmarkDecimal_Sub_same_precision(b *testing.B) { +func Benchmark_decimal_Decimal_Sub_same_precision(b *testing.B) { d1 := NewFromFloat(1000.123) d2 := NewFromFloat(500.123) b.ReportAllocs() b.StartTimer() for i := 0; i < b.N; i++ { - d1.Sub(d2) - } -} - -func BenchmarkDecimal_IsInteger(b *testing.B) { - d := RequireFromString("12.000") - - b.ReportAllocs() - b.StartTimer() - for i := 0; i < b.N; i++ { - d.IsInteger() - } -} - -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_PowNegativeExponent(b *testing.B) { - d1 := RequireFromString("1.0041666666666667") - d2 := RequireFromString("-360") - - for i := 0; i < b.N; i++ { - d1.Pow(d2) - } -} - -func BenchmarkDecimal_NewFromString(b *testing.B) { - count := 72 - prices := make([]string, 0, count) - for i := 1; i <= count; i++ { - prices = append(prices, fmt.Sprintf("%d.%d", i*100, i)) - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - for _, p := range prices { - d, err := NewFromString(p) - if err != nil { - b.Log(d) - b.Error(err) - } - } - } -} - -func BenchmarkDecimal_NewFromString_large_number(b *testing.B) { - count := 72 - prices := make([]string, 0, count) - for i := 1; i <= count; i++ { - prices = append(prices, "9323372036854775807.9223372036854775807") - } - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - for _, p := range prices { - d, err := NewFromString(p) - if err != nil { - b.Log(d) - b.Error(err) - } - } - } -} - -func BenchmarkDecimal_ExpHullAbraham(b *testing.B) { - b.ResetTimer() - - d := RequireFromString("30.412346346346") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = d.ExpHullAbrham(10) - } -} - -func BenchmarkDecimal_ExpTaylor(b *testing.B) { - b.ResetTimer() - - d := RequireFromString("30.412346346346") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = d.ExpTaylor(10) - } -} - -func BenchmarkDecimal_Normalize(b *testing.B) { - for _, c := range []struct { - name string - d Decimal - }{ - {"NoZeros", RequireFromString("123.456")}, - {"OneZero", RequireFromString("12.50")}, - {"Div", NewFromInt(5).Div(NewFromInt(2))}, - {"Big", RequireFromString("123456789012345678901234567890.5000")}, - {"ManyZeros", RequireFromString("1.5").Round(10000)}, - } { - b.Run(c.name, func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - c.d.Normalize() - } - }) - } -} - -func BenchmarkDecimal_Sqrt(b *testing.B) { - for _, c := range []struct { - name string - d Decimal - places int32 - }{ - {"Small", RequireFromString("0.002342"), 16}, - {"Int", NewFromInt(2), 16}, - {"Big", RequireFromString("123456789012345678901234567890.123"), 16}, - {"Places1000", NewFromInt(2), 1000}, - } { - b.Run(c.name, func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - _, _ = c.d.SqrtRound(c.places) - } - }) - } -} - -func BenchmarkDecimal_UnmarshalJSON(b *testing.B) { - b.ResetTimer() - - bstr := []byte("1234.56789") - - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _ = (&Decimal{}).UnmarshalJSON(bstr) + d1.Add(d2) } } diff --git a/decimal_go113_test.go b/decimal_go113_test.go deleted file mode 100644 index b6132be..0000000 --- a/decimal_go113_test.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build go1.13 -// +build go1.13 - -package decimal - -import ( - "math" - "math/rand" - "strconv" - "testing" -) - -// Before Go 1.13, strconv could return a shortest form that wasn't the nearest one for these (golang/go#29491). -func init() { - testTable = append(testTable, - &testEnt{498484681984085570, "498484681984085570", "", ""}, - &testEnt{5.8339553793802237e+23, "583395537938022370000000", "", ""}, - ) -} - -// TestNewFromFloatShortestRoundTrip pins the contract of NewFromFloat and -// NewFromFloat32: the result is the shortest decimal that parses back to the -// original float. It covers the exponent and significand shapes a -// shortest-representation conversion has to get right, namely subnormals, -// three-digit exponents, single-digit significands and the range extremes. -// -// It is gated on Go 1.13 because before that strconv itself could return a -// shortest form that was not the nearest one (golang/go#29491), which would make -// the reference value below wrong rather than the conversion under test. -func TestNewFromFloatShortestRoundTrip(t *testing.T) { - f64 := []float64{ - 1, -1, 0.1, -0.1, 0.5, 2.5, 100, 123.456, - 1e15, 1e16, 1e17, 1e21, 1e22, 1e23, 9007199254740992, - 1e-100, 1e100, 1e-300, 1e300, - math.MaxFloat64, -math.MaxFloat64, - math.SmallestNonzeroFloat64, -math.SmallestNonzeroFloat64, - math.Pi, math.E, - } - // Every power of two, which sweeps the whole exponent range including the - // subnormals, plus both neighbours of every value collected so far. - for e := -1074; e <= 1023; e++ { - f64 = append(f64, math.Ldexp(1, e)) - } - for _, f := range append([]float64{}, f64...) { - f64 = append(f64, math.Nextafter(f, math.Inf(1)), math.Nextafter(f, math.Inf(-1))) - } - - for _, f := range f64 { - if f == 0 || math.IsInf(f, 0) { - continue - } - want, err := NewFromString(strconv.FormatFloat(f, 'f', -1, 64)) - if err != nil { - t.Fatalf("NewFromString(%v): %v", f, err) - } - got := NewFromFloat(f) - if !got.Equal(want) { - t.Errorf("NewFromFloat(%v) = %s (%s, %d), want %s (%s, %d)", - f, got, got.value, got.exp, want, want.value, want.exp) - } - if back, _ := got.Float64(); back != f { - t.Errorf("NewFromFloat(%v).Float64() = %v, does not round-trip", f, back) - } - } - - f32 := []float32{ - 1, -1, 0.1, -0.1, 100, 123.456, - math.MaxFloat32, -math.MaxFloat32, - math.SmallestNonzeroFloat32, -math.SmallestNonzeroFloat32, - } - for e := -149; e <= 127; e++ { - f32 = append(f32, float32(math.Ldexp(1, e))) - } - for _, f := range f32 { - if f == 0 { - continue - } - want, err := NewFromString(strconv.FormatFloat(float64(f), 'f', -1, 32)) - if err != nil { - t.Fatalf("NewFromString(%v): %v", f, err) - } - got := NewFromFloat32(f) - if !got.Equal(want) { - t.Errorf("NewFromFloat32(%v) = %s (%s, %d), want %s (%s, %d)", - f, got, got.value, got.exp, want, want.value, want.exp) - } - } -} - -// TestNewFromFloatRandomRoundTrip sweeps random bit patterns, which puts a -// meaningful share of subnormals and extreme exponents through the conversion. -func TestNewFromFloatRandomRoundTrip(t *testing.T) { - rng := rand.New(rand.NewSource(0xdead1337)) - for i := 0; i < 200000; i++ { - f := math.Float64frombits(rng.Uint64()) - if f == 0 || math.IsNaN(f) || math.IsInf(f, 0) { - continue - } - want, err := NewFromString(strconv.FormatFloat(f, 'f', -1, 64)) - if err != nil { - t.Fatalf("NewFromString(%v): %v", f, err) - } - if got := NewFromFloat(f); !got.Equal(want) { - t.Fatalf("NewFromFloat(%v) = %s (%s, %d), want %s (%s, %d)", - f, got, got.value, got.exp, want, want.value, want.exp) - } - } - for i := 0; i < 200000; i++ { - f := math.Float32frombits(rng.Uint32()) - if f == 0 || math.IsNaN(float64(f)) || math.IsInf(float64(f), 0) { - continue - } - want, err := NewFromString(strconv.FormatFloat(float64(f), 'f', -1, 32)) - if err != nil { - t.Fatalf("NewFromString(%v): %v", f, err) - } - if got := NewFromFloat32(f); !got.Equal(want) { - t.Fatalf("NewFromFloat32(%v) = %s (%s, %d), want %s (%s, %d)", - f, got, got.value, got.exp, want, want.value, want.exp) - } - } -} diff --git a/decimal_go124_test.go b/decimal_go124_test.go deleted file mode 100644 index 13ee324..0000000 --- a/decimal_go124_test.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build go1.24 -// +build go1.24 - -package decimal - -import ( - "encoding/json" - "testing" -) - -// `omitzero` is supported by encoding/json starting in Go 1.24. -// Keep this test in a go1.24-gated file so older CI jobs skip it. -func TestJSONOmitZeroTag(t *testing.T) { - type Nested struct { - Amount Decimal `json:"amount,omitzero"` - } - - type Parent struct { - Nested Nested `json:"nested,omitzero"` - } - - tests := []struct { - name string - parent Parent - expected string - }{ - { - name: "Decimal{} empty value", - parent: Parent{ - Nested: Nested{ - Amount: Decimal{}, - }, - }, - expected: "{}", - }, - { - name: "Zero constant", - parent: Parent{ - Nested: Nested{ - Amount: Zero, - }, - }, - expected: "{}", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - b, err := json.Marshal(tt.parent) - if err != nil { - t.Fatal(err) - } - if string(b) != tt.expected { - t.Errorf("expected %s, got %s", tt.expected, string(b)) - } - }) - } -} diff --git a/decimal_test.go b/decimal_test.go index 73d43c8..488d405 100644 --- a/decimal_test.go +++ b/decimal_test.go @@ -2,7 +2,6 @@ package decimal import ( "database/sql/driver" - "encoding/hex" "encoding/json" "encoding/xml" "fmt" @@ -10,10 +9,8 @@ import ( "math/big" "math/rand" "reflect" - "regexp" "strconv" "strings" - "sync" "testing" "testing/quick" "time" @@ -79,11 +76,6 @@ var testTableScientificNotation = map[string]string{ "123.456e10": "1234560000000", } -var testMalformedDecimalStrings = map[string]error{ - "1ee10": fmt.Errorf("can't convert %s to decimal: multiple 'E' characters found", "1ee10"), - "123.45.66": fmt.Errorf("can't convert %s to decimal: too many .s", "123.45.66"), -} - func init() { for _, s := range testTable { s.exact = strconv.FormatFloat(s.float, 'f', 1500, 64) @@ -243,44 +235,6 @@ func TestNewFromString(t *testing.T) { d.value.String(), d.exp) } } - - for s, e := range testMalformedDecimalStrings { - _, err := NewFromString(s) - if err == nil { - t.Errorf("expected an error, got nil %s", s) - } else if err.Error() != e.Error() { - t.Errorf("expected %v error, got %v", e, err) - } - } -} - -func TestNewFromFormattedString(t *testing.T) { - for _, testCase := range []struct { - Formatted string - Expected string - ReplRegex *regexp.Regexp - }{ - {"$10.99", "10.99", regexp.MustCompile("[$]")}, - {"$ 12.1", "12.1", regexp.MustCompile(`[$\s]`)}, - {"$61,690.99", "61690.99", regexp.MustCompile("[$,]")}, - {"1_000_000.00", "1000000.00", regexp.MustCompile("[_]")}, - {"41,410.00", "41410.00", regexp.MustCompile("[,]")}, - {"5200 USD", "5200", regexp.MustCompile(`[USD\s]`)}, - } { - dFormatted, err := NewFromFormattedString(testCase.Formatted, testCase.ReplRegex) - if err != nil { - t.Fatal(err) - } - - dExact, err := NewFromString(testCase.Expected) - if err != nil { - t.Fatal(err) - } - - if !dFormatted.Equal(dExact) { - t.Errorf("expect %s, got %s", dExact, dFormatted) - } - } } func TestFloat64(t *testing.T) { @@ -305,76 +259,6 @@ func TestFloat64(t *testing.T) { } } -func TestFloat64OutOfRange(t *testing.T) { - type tc struct { - d Decimal - f float64 - exact bool - } - tests := []tc{ - // near the float64 limits, still converted the normal way - {New(1, 308), 1e308, false}, - {New(-1, 308), -1e308, false}, - {New(1, 309), math.Inf(1), false}, - {New(-1, 309), math.Inf(-1), false}, - {New(5, -324), 5e-324, false}, - {New(1, -325), 0, false}, - {New(-1, -325), math.Copysign(0, -1), false}, - {New(123456789, -340), 1.23456789e-332, false}, - {New(0, -1<<30), 0, true}, - {New(0, 1<<30), 0, true}, - // far outside the float64 range (issue #226) - {New(100, -1<<30), 0, false}, - {New(-100, -1<<30), math.Copysign(0, -1), false}, - {New(100, 1<<30), math.Inf(1), false}, - {New(-100, 1<<30), math.Inf(-1), false}, - {New(1, math.MinInt32), 0, false}, - {New(1, math.MaxInt32), math.Inf(1), false}, - {NewFromBigInt(new(big.Int).Exp(tenInt, big.NewInt(400), nil), -1<<30), 0, false}, - } - - for _, test := range tests { - done := make(chan struct{}) - var f float64 - var exact bool - go func() { - f, exact = test.d.Float64() - close(done) - }() - - select { - case <-done: - case <-time.After(5 * time.Second): - t.Fatalf("Float64() of %de%d did not return within 5s", test.d.Coefficient(), test.d.Exponent()) - } - - if math.Float64bits(f) != math.Float64bits(test.f) || exact != test.exact { - t.Errorf("%de%d: expected (%v, %v), got (%v, %v)", - test.d.Coefficient(), test.d.Exponent(), test.f, test.exact, f, exact) - } - } -} - -func TestNewFromStringUnicodeMinus(t *testing.T) { - cases := []struct { - in, want string - }{ - {"\u2212123.45", "-123.45"}, - {"\u2212.5", "-0.5"}, - {"1e\u22122", "0.01"}, - } - for _, tc := range cases { - d, err := NewFromString(tc.in) - if err != nil { - t.Errorf("NewFromString(%q) unexpected error: %v", tc.in, err) - continue - } - if d.String() != tc.want { - t.Errorf("NewFromString(%q) = %q, want %q", tc.in, d.String(), tc.want) - } - } -} - func TestNewFromStringErrs(t *testing.T) { tests := []string{ "", @@ -407,14 +291,6 @@ func TestNewFromStringErrs(t *testing.T) { "123.456Easdf", "123.456e" + strconv.FormatInt(math.MinInt64, 10), "123.456e" + strconv.FormatInt(math.MinInt32, 10), - "512.99 USD", - "$99.99", - "51,850.00", - "20_000_000.00", - "$20_000_000.00", - ".-5", - ".+5", - ".-1234567890123456789012", } for _, s := range tests { @@ -426,67 +302,6 @@ func TestNewFromStringErrs(t *testing.T) { } } -func TestParseInt64SkipIndex(t *testing.T) { - // every string of up to 5 bytes from this alphabet, with the decimal point at each position - const alphabet = "059-+.a " - var check func(s string) - check = func(s string) { - for skip := -1; skip < len(s); skip++ { - if skip >= 0 && s[skip] != '.' { - continue - } - if skip == 0 && len(s) > 1 && (s[1] == '-' || s[1] == '+') { - continue // NewFromString rejects a sign after the point before parsing - } - withoutPoint := s - if skip >= 0 { - withoutPoint = s[:skip] + s[skip+1:] - } - want, err := strconv.ParseInt(withoutPoint, 10, 64) - got, ok := parseInt64SkipIndex(s, skip) - if ok != (err == nil) || got != want { - t.Fatalf("parseInt64SkipIndex(%q, %d) = %d, %v; ParseInt(%q) = %d, %v", s, skip, got, ok, withoutPoint, want, err) - } - } - if len(s) < 5 { - for i := range alphabet { - check(s + alphabet[i:i+1]) - } - } - } - check("") -} - -func TestMaxDecodeExponent(t *testing.T) { - limit := int32(MaxDecodeExponent) - for _, c := range []struct { - exp int32 - ok bool - }{ - {limit, true}, - {-limit, true}, - {limit + 1, false}, - {-limit - 1, false}, - {math.MaxInt32, false}, - {math.MinInt32, false}, - } { - if _, err := NewFromString(fmt.Sprintf("1e%d", c.exp)); (err == nil) != c.ok { - t.Errorf("NewFromString(1e%d): got error %v, want ok=%v", c.exp, err, c.ok) - } - - b, _ := New(1, c.exp).MarshalBinary() - d := New(7, 0) - err := d.UnmarshalBinary(b) - if (err == nil) != c.ok || (!c.ok && d.Exponent() != 0) { - t.Errorf("UnmarshalBinary(exp %d): got error %v and exp %d, want ok=%v", c.exp, err, d.Exponent(), c.ok) - } - } - - if _, err := NewFromString(fmt.Sprintf("0.1e%d", -limit)); err == nil { - t.Errorf("NewFromString(0.1e%d) accepted exponent %d", -limit, -limit-1) - } -} - func TestNewFromStringDeepEquals(t *testing.T) { type StrCmp struct { str1 string @@ -535,15 +350,16 @@ func TestRequireFromString(t *testing.T) { func TestRequireFromStringErrs(t *testing.T) { s := "qwert" + var d Decimal var err interface{} - func() { + func(d Decimal) { defer func() { err = recover() }() RequireFromString(s) - }() + }(d) if err == nil { t.Errorf("panic expected when parsing %s", s) @@ -622,11 +438,10 @@ func TestNewFromFloatWithExponent(t *testing.T) { func TestNewFromInt(t *testing.T) { tests := map[int64]string{ - 0: "0", - 1: "1", - 323412345: "323412345", - 9223372036854775807: "9223372036854775807", - -9223372036854775808: "-9223372036854775808", + 0: "0", + 1: "1", + 323412345: "323412345", + 9223372036854775807: "9223372036854775807", } // add negatives @@ -648,11 +463,10 @@ func TestNewFromInt(t *testing.T) { func TestNewFromInt32(t *testing.T) { tests := map[int32]string{ - 0: "0", - 1: "1", - 323412345: "323412345", - 2147483647: "2147483647", - -2147483648: "-2147483648", + 0: "0", + 1: "1", + 323412345: "323412345", + 2147483647: "2147483647", } // add negatives @@ -672,25 +486,6 @@ 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) { type Inp struct { val *big.Int @@ -708,7 +503,7 @@ func TestNewFromBigIntWithExponent(t *testing.T) { // add negatives for p, s := range tests { - if p.val.Cmp(zeroInt) > 0 { + if p.val.Cmp(Zero.value) > 0 { tests[Inp{p.val.Neg(p.val), p.exp}] = "-" + s } } @@ -723,154 +518,6 @@ 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 TestNewFromBigFloat(t *testing.T) { - mustParseFloat := func(s string, prec uint) *big.Float { - f, _, err := big.ParseFloat(s, 10, prec, big.ToNearestEven) - if err != nil { - t.Fatalf("failed to parse float %s: %v", s, err) - } - return f - } - - testCases := []struct { - name string - input *big.Float - expected string - }{ - {"zero", new(big.Float).SetInt64(0), "0"}, - {"uninitialized", new(big.Float), "0"}, - {"negative zero", new(big.Float).Neg(new(big.Float)), "0"}, - {"integer positive", new(big.Float).SetInt64(12345), "12345"}, - {"integer negative", new(big.Float).SetInt64(-12345), "-12345"}, - {"simple float positive", mustParseFloat("12.1", 64), "12.1"}, - {"simple float negative", mustParseFloat("-12.1", 64), "-12.1"}, - {"fractional positive", mustParseFloat("0.01", 64), "0.01"}, - {"fractional negative", mustParseFloat("-0.01", 64), "-0.01"}, - {"decimal 123.456", mustParseFloat("123.456", 64), "123.456"}, - {"decimal -123.456", mustParseFloat("-123.456", 64), "-123.456"}, - {"from float64 exact", new(big.Float).SetFloat64(1.25), "1.25"}, - {"from float64 0.5", new(big.Float).SetFloat64(0.5), "0.5"}, - {"from rat half", new(big.Float).SetRat(big.NewRat(1, 2)), "0.5"}, - {"high precision pi", mustParseFloat("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 512), "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679"}, - {"high precision large", mustParseFloat("1234567890123456789012345678901234567890.1234567890123456789", 512), "1234567890123456789012345678901234567890.1234567890123456789"}, - {"large exponent", mustParseFloat("1e25", 256), "10000000000000000000000000"}, - {"small exponent", mustParseFloat("1e-25", 256), "0.0000000000000000000000001"}, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - d, err := NewFromBigFloat(tc.input) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if d.String() != tc.expected { - t.Errorf("expected %s, got %s", tc.expected, d.String()) - } - }) - } -} - -func TestNewFromBigFloat_Error(t *testing.T) { - t.Run("nil", func(t *testing.T) { - _, err := NewFromBigFloat(nil) - if err == nil { - t.Fatal("expected error for nil, got nil") - } - }) - - t.Run("positive inf", func(t *testing.T) { - f := new(big.Float).SetInf(false) - _, err := NewFromBigFloat(f) - if err == nil { - t.Fatal("expected error for +Inf, got nil") - } - }) - - t.Run("negative inf", func(t *testing.T) { - f := new(big.Float).SetInf(true) - _, err := NewFromBigFloat(f) - if err == nil { - t.Fatal("expected error for -Inf, got nil") - } - }) - - t.Run("exponent overflow", func(t *testing.T) { - f, _, err := big.ParseFloat("1e15000", 10, 64, big.ToNearestEven) - if err == nil { - _, err = NewFromBigFloat(f) - if err == nil { - t.Fatal("expected error for exponent exceeding MaxDecodeExponent, got nil") - } - } - }) -} - -func TestCopy(t *testing.T) { - origin := New(1, 0) - cpy := origin.Copy() - - if origin.value == cpy.value { - t.Error("expecting copy and origin to have different value pointers") - } - - if cpy.Cmp(origin) != 0 { - t.Error("expecting copy and origin to be equals, but they are not") - } - - // change value - cpy = cpy.Add(New(1, 0)) - - if cpy.Cmp(origin) == 0 { - t.Error("expecting copy and origin to have different values, but they are equal") - } -} - func TestJSON(t *testing.T) { for _, x := range testTable { s := x.short @@ -1031,17 +678,6 @@ func TestNullDecimalBadJSON(t *testing.T) { if err == nil { t.Errorf("expected error, got %+v", doc) } - if doc.Amount.Valid { - t.Errorf("%s: expected Valid to be false after an error", testCase) - } - } - - var nd NullDecimal - if err := nd.Scan("nope"); err == nil || nd.Valid { - t.Errorf("Scan(\"nope\"): expected an error and Valid false, got %v and %v", err, nd.Valid) - } - if err := nd.DecodeSpanner("nope"); err == nil || nd.Valid { - t.Errorf("DecodeSpanner(\"nope\"): expected an error and Valid false, got %v and %v", err, nd.Valid) } } @@ -1092,96 +728,6 @@ func TestBadXML(t *testing.T) { } } -func TestNullDecimalXML(t *testing.T) { - // test valid values - for _, x := range testTable { - s := x.short - var doc struct { - XMLName xml.Name `xml:"account"` - Amount NullDecimal `xml:"amount"` - } - docStr := `` + s + `` - err := xml.Unmarshal([]byte(docStr), &doc) - if err != nil { - t.Errorf("error unmarshaling %s: %v", docStr, err) - } else if doc.Amount.Decimal.String() != s { - t.Errorf("expected %s, got %s (%s, %d)", - s, doc.Amount.Decimal.String(), - doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp) - } - - out, err := xml.Marshal(&doc) - if err != nil { - t.Errorf("error marshaling %+v: %v", doc, err) - } else if string(out) != docStr { - t.Errorf("expected %s, got %s", docStr, string(out)) - } - } - - var doc struct { - XMLName xml.Name `xml:"account"` - Amount NullDecimal `xml:"amount"` - } - - // test for XML with empty body - docStr := `` - err := xml.Unmarshal([]byte(docStr), &doc) - if err != nil { - t.Errorf("error unmarshaling: %s: %v", docStr, err) - } else if doc.Amount.Valid { - t.Errorf("expected null value to have Valid = false, got Valid = true and Decimal = %s (%s, %d)", - doc.Amount.Decimal.String(), - doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp) - } - - expected := `` - out, err := xml.Marshal(&doc) - if err != nil { - t.Errorf("error marshaling %+v: %v", doc, err) - } else if string(out) != expected { - t.Errorf("expected %s, got %s", expected, string(out)) - } - - // test for empty XML - docStr = `` - err = xml.Unmarshal([]byte(docStr), &doc) - if err != nil { - t.Errorf("error unmarshaling: %s: %v", docStr, err) - } else if doc.Amount.Valid { - t.Errorf("expected null value to have Valid = false, got Valid = true and Decimal = %s (%s, %d)", - doc.Amount.Decimal.String(), - doc.Amount.Decimal.value.String(), doc.Amount.Decimal.exp) - } - - expected = `` - out, err = xml.Marshal(&doc) - if err != nil { - t.Errorf("error marshaling %+v: %v", doc, err) - } else if string(out) != expected { - t.Errorf("expected %s, got %s", expected, string(out)) - } -} - -func TestNullDecimalBadXML(t *testing.T) { - for _, testCase := range []string{ - "o_o", - "7", - ``, - `nope`, - `0.333`, - } { - var doc struct { - XMLName xml.Name `xml:"account"` - Amount NullDecimal `xml:"amount"` - } - err := xml.Unmarshal([]byte(testCase), &doc) - if err == nil { - t.Errorf("expected error, got %+v", doc) - } - } -} - func TestDecimal_rescale(t *testing.T) { type Inp struct { int int64 @@ -1384,13 +930,13 @@ func TestDecimal_RoundAndStringFixed(t *testing.T) { for _, test := range tests { d, err := NewFromString(test.input) if err != nil { - t.Fatal(err) + panic(err) } // test Round expected, err := NewFromString(test.expected) if err != nil { - t.Fatal(err) + panic(err) } got := d.Round(test.places) if !got.Equal(expected) { @@ -1410,370 +956,6 @@ func TestDecimal_RoundAndStringFixed(t *testing.T) { } } -func TestDecimal_RoundCeilAndStringFixed(t *testing.T) { - type testData struct { - input string - places int32 - expected string - expectedFixed string - } - tests := []testData{ - {"1.454", 0, "2", ""}, - {"1.454", 1, "1.5", ""}, - {"1.454", 2, "1.46", ""}, - {"1.454", 3, "1.454", ""}, - {"1.454", 4, "1.454", "1.4540"}, - {"1.454", 5, "1.454", "1.45400"}, - {"1.554", 0, "2", ""}, - {"1.554", 1, "1.6", ""}, - {"1.554", 2, "1.56", ""}, - {"0.554", 0, "1", ""}, - {"0.454", 0, "1", ""}, - {"0.454", 5, "0.454", "0.45400"}, - {"0", 0, "0", ""}, - {"0", 1, "0", "0.0"}, - {"0", 2, "0", "0.00"}, - {"0", -1, "0", ""}, - {"5", 2, "5", "5.00"}, - {"5", 1, "5", "5.0"}, - {"5", 0, "5", ""}, - {"500", 2, "500", "500.00"}, - {"500", -2, "500", ""}, - {"545", -1, "550", ""}, - {"545", -2, "600", ""}, - {"545", -3, "1000", ""}, - {"545", -4, "10000", ""}, - {"499", -3, "1000", ""}, - {"499", -4, "10000", ""}, - {"1.1001", 2, "1.11", ""}, - {"-1.1001", 2, "-1.10", ""}, - {"-1.454", 0, "-1", ""}, - {"-1.454", 1, "-1.4", ""}, - {"-1.454", 2, "-1.45", ""}, - {"-1.454", 3, "-1.454", ""}, - {"-1.454", 4, "-1.454", "-1.4540"}, - {"-1.454", 5, "-1.454", "-1.45400"}, - {"-1.554", 0, "-1", ""}, - {"-1.554", 1, "-1.5", ""}, - {"-1.554", 2, "-1.55", ""}, - {"-0.554", 0, "0", ""}, - {"-0.454", 0, "0", ""}, - {"-0.454", 5, "-0.454", "-0.45400"}, - {"-5", 2, "-5", "-5.00"}, - {"-5", 1, "-5", "-5.0"}, - {"-5", 0, "-5", ""}, - {"-500", 2, "-500", "-500.00"}, - {"-500", -2, "-500", ""}, - {"-545", -1, "-540", ""}, - {"-545", -2, "-500", ""}, - {"-545", -3, "0", ""}, - {"-545", -4, "0", ""}, - {"-499", -3, "0", ""}, - {"-499", -4, "0", ""}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } - - // test Round - expected, err := NewFromString(test.expected) - if err != nil { - t.Fatal(err) - } - got := d.RoundCeil(test.places) - if !got.Equal(expected) { - t.Errorf("Rounding ceil %s to %d places, got %s, expected %s", - d, test.places, got, expected) - } - - // test StringFixed - if test.expectedFixed == "" { - test.expectedFixed = test.expected - } - gotStr := got.StringFixed(test.places) - if gotStr != test.expectedFixed { - t.Errorf("(%s).StringFixed(%d): got %s, expected %s", - d, test.places, gotStr, test.expectedFixed) - } - } -} - -func TestDecimal_RoundFloorAndStringFixed(t *testing.T) { - type testData struct { - input string - places int32 - expected string - expectedFixed string - } - tests := []testData{ - {"1.454", 0, "1", ""}, - {"1.454", 1, "1.4", ""}, - {"1.454", 2, "1.45", ""}, - {"1.454", 3, "1.454", ""}, - {"1.454", 4, "1.454", "1.4540"}, - {"1.454", 5, "1.454", "1.45400"}, - {"1.554", 0, "1", ""}, - {"1.554", 1, "1.5", ""}, - {"1.554", 2, "1.55", ""}, - {"0.554", 0, "0", ""}, - {"0.454", 0, "0", ""}, - {"0.454", 5, "0.454", "0.45400"}, - {"0", 0, "0", ""}, - {"0", 1, "0", "0.0"}, - {"0", 2, "0", "0.00"}, - {"0", -1, "0", ""}, - {"5", 2, "5", "5.00"}, - {"5", 1, "5", "5.0"}, - {"5", 0, "5", ""}, - {"500", 2, "500", "500.00"}, - {"500", -2, "500", ""}, - {"545", -1, "540", ""}, - {"545", -2, "500", ""}, - {"545", -3, "0", ""}, - {"545", -4, "0", ""}, - {"499", -3, "0", ""}, - {"499", -4, "0", ""}, - {"1.1001", 2, "1.10", ""}, - {"-1.1001", 2, "-1.11", ""}, - {"-1.454", 0, "-2", ""}, - {"-1.454", 1, "-1.5", ""}, - {"-1.454", 2, "-1.46", ""}, - {"-1.454", 3, "-1.454", ""}, - {"-1.454", 4, "-1.454", "-1.4540"}, - {"-1.454", 5, "-1.454", "-1.45400"}, - {"-1.554", 0, "-2", ""}, - {"-1.554", 1, "-1.6", ""}, - {"-1.554", 2, "-1.56", ""}, - {"-0.554", 0, "-1", ""}, - {"-0.454", 0, "-1", ""}, - {"-0.454", 5, "-0.454", "-0.45400"}, - {"-5", 2, "-5", "-5.00"}, - {"-5", 1, "-5", "-5.0"}, - {"-5", 0, "-5", ""}, - {"-500", 2, "-500", "-500.00"}, - {"-500", -2, "-500", ""}, - {"-545", -1, "-550", ""}, - {"-545", -2, "-600", ""}, - {"-545", -3, "-1000", ""}, - {"-545", -4, "-10000", ""}, - {"-499", -3, "-1000", ""}, - {"-499", -4, "-10000", ""}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } - - // test Round - expected, err := NewFromString(test.expected) - if err != nil { - t.Fatal(err) - } - got := d.RoundFloor(test.places) - if !got.Equal(expected) { - t.Errorf("Rounding floor %s to %d places, got %s, expected %s", - d, test.places, got, expected) - } - - // test StringFixed - if test.expectedFixed == "" { - test.expectedFixed = test.expected - } - gotStr := got.StringFixed(test.places) - if gotStr != test.expectedFixed { - t.Errorf("(%s).StringFixed(%d): got %s, expected %s", - d, test.places, gotStr, test.expectedFixed) - } - } -} - -func TestDecimal_RoundUpAndStringFixed(t *testing.T) { - type testData struct { - input string - places int32 - expected string - expectedFixed string - } - tests := []testData{ - {"1.454", 0, "2", ""}, - {"1.454", 1, "1.5", ""}, - {"1.454", 2, "1.46", ""}, - {"1.454", 3, "1.454", ""}, - {"1.454", 4, "1.454", "1.4540"}, - {"1.454", 5, "1.454", "1.45400"}, - {"1.554", 0, "2", ""}, - {"1.554", 1, "1.6", ""}, - {"1.554", 2, "1.56", ""}, - {"0.554", 0, "1", ""}, - {"0.454", 0, "1", ""}, - {"0.454", 5, "0.454", "0.45400"}, - {"0", 0, "0", ""}, - {"0", 1, "0", "0.0"}, - {"0", 2, "0", "0.00"}, - {"0", -1, "0", ""}, - {"5", 2, "5", "5.00"}, - {"5", 1, "5", "5.0"}, - {"5", 0, "5", ""}, - {"500", 2, "500", "500.00"}, - {"500", -2, "500", ""}, - {"545", -1, "550", ""}, - {"545", -2, "600", ""}, - {"545", -3, "1000", ""}, - {"545", -4, "10000", ""}, - {"499", -3, "1000", ""}, - {"499", -4, "10000", ""}, - {"1.1001", 2, "1.11", ""}, - {"-1.1001", 2, "-1.11", ""}, - {"-1.454", 0, "-2", ""}, - {"-1.454", 1, "-1.5", ""}, - {"-1.454", 2, "-1.46", ""}, - {"-1.454", 3, "-1.454", ""}, - {"-1.454", 4, "-1.454", "-1.4540"}, - {"-1.454", 5, "-1.454", "-1.45400"}, - {"-1.554", 0, "-2", ""}, - {"-1.554", 1, "-1.6", ""}, - {"-1.554", 2, "-1.56", ""}, - {"-0.554", 0, "-1", ""}, - {"-0.454", 0, "-1", ""}, - {"-0.454", 5, "-0.454", "-0.45400"}, - {"-5", 2, "-5", "-5.00"}, - {"-5", 1, "-5", "-5.0"}, - {"-5", 0, "-5", ""}, - {"-500", 2, "-500", "-500.00"}, - {"-500", -2, "-500", ""}, - {"-545", -1, "-550", ""}, - {"-545", -2, "-600", ""}, - {"-545", -3, "-1000", ""}, - {"-545", -4, "-10000", ""}, - {"-499", -3, "-1000", ""}, - {"-499", -4, "-10000", ""}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } - - // test Round - expected, err := NewFromString(test.expected) - if err != nil { - t.Fatal(err) - } - got := d.RoundUp(test.places) - if !got.Equal(expected) { - t.Errorf("Rounding up %s to %d places, got %s, expected %s", - d, test.places, got, expected) - } - - // test StringFixed - if test.expectedFixed == "" { - test.expectedFixed = test.expected - } - gotStr := got.StringFixed(test.places) - if gotStr != test.expectedFixed { - t.Errorf("(%s).StringFixed(%d): got %s, expected %s", - d, test.places, gotStr, test.expectedFixed) - } - } -} - -func TestDecimal_RoundDownAndStringFixed(t *testing.T) { - type testData struct { - input string - places int32 - expected string - expectedFixed string - } - tests := []testData{ - {"1.454", 0, "1", ""}, - {"1.454", 1, "1.4", ""}, - {"1.454", 2, "1.45", ""}, - {"1.454", 3, "1.454", ""}, - {"1.454", 4, "1.454", "1.4540"}, - {"1.454", 5, "1.454", "1.45400"}, - {"1.554", 0, "1", ""}, - {"1.554", 1, "1.5", ""}, - {"1.554", 2, "1.55", ""}, - {"0.554", 0, "0", ""}, - {"0.454", 0, "0", ""}, - {"0.454", 5, "0.454", "0.45400"}, - {"0", 0, "0", ""}, - {"0", 1, "0", "0.0"}, - {"0", 2, "0", "0.00"}, - {"0", -1, "0", ""}, - {"5", 2, "5", "5.00"}, - {"5", 1, "5", "5.0"}, - {"5", 0, "5", ""}, - {"500", 2, "500", "500.00"}, - {"500", -2, "500", ""}, - {"545", -1, "540", ""}, - {"545", -2, "500", ""}, - {"545", -3, "0", ""}, - {"545", -4, "0", ""}, - {"499", -3, "0", ""}, - {"499", -4, "0", ""}, - {"1.1001", 2, "1.10", ""}, - {"-1.1001", 2, "-1.10", ""}, - {"-1.454", 0, "-1", ""}, - {"-1.454", 1, "-1.4", ""}, - {"-1.454", 2, "-1.45", ""}, - {"-1.454", 3, "-1.454", ""}, - {"-1.454", 4, "-1.454", "-1.4540"}, - {"-1.454", 5, "-1.454", "-1.45400"}, - {"-1.554", 0, "-1", ""}, - {"-1.554", 1, "-1.5", ""}, - {"-1.554", 2, "-1.55", ""}, - {"-0.554", 0, "0", ""}, - {"-0.454", 0, "0", ""}, - {"-0.454", 5, "-0.454", "-0.45400"}, - {"-5", 2, "-5", "-5.00"}, - {"-5", 1, "-5", "-5.0"}, - {"-5", 0, "-5", ""}, - {"-500", 2, "-500", "-500.00"}, - {"-500", -2, "-500", ""}, - {"-545", -1, "-540", ""}, - {"-545", -2, "-500", ""}, - {"-545", -3, "0", ""}, - {"-545", -4, "0", ""}, - {"-499", -3, "0", ""}, - {"-499", -4, "0", ""}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } - - // test Round - expected, err := NewFromString(test.expected) - if err != nil { - t.Fatal(err) - } - got := d.RoundDown(test.places) - if !got.Equal(expected) { - t.Errorf("Rounding down %s to %d places, got %s, expected %s", - d, test.places, got, expected) - } - - // test StringFixed - if test.expectedFixed == "" { - test.expectedFixed = test.expected - } - gotStr := got.StringFixed(test.places) - if gotStr != test.expectedFixed { - t.Errorf("(%s).StringFixed(%d): got %s, expected %s", - d, test.places, gotStr, test.expectedFixed) - } - } -} - func TestDecimal_BankRoundAndStringFixed(t *testing.T) { type testData struct { input string @@ -2069,64 +1251,6 @@ func TestDecimal_Shift(t *testing.T) { } } -func TestDecimal_ShiftExponentOverflow(t *testing.T) { - // The sum still fits, so the exponent is unchanged by the overflow check. - got := New(5, math.MaxInt32-1).Shift(1) - if !got.Equal(New(5, math.MaxInt32)) { - t.Fatalf("shift onto MaxInt32: exp %d", got.Exponent()) - } - - if !didPanic(func() { New(5, math.MaxInt32).Shift(1) }) { - t.Fatalf("should have gotten an overflow panic") - } - if !didPanic(func() { New(5, math.MinInt32).Shift(-1) }) { - t.Fatalf("should have gotten an overflow panic") - } -} - -func TestDecimal_Normalize(t *testing.T) { - for _, c := range []struct { - d Decimal - coef string - exp int32 - }{ - {New(123, -2), "123", -2}, - {New(100, 0), "1", 2}, - {New(100, -3), "1", -1}, - {New(-1500, -3), "-15", -1}, - {RequireFromString("2.5000000000000000"), "25", -1}, - {RequireFromString("-123456789012345678901234567891000"), "-123456789012345678901234567891", 3}, - {RequireFromString("12.500000000000000000"), "125", -1}, - {RequireFromString("1" + strings.Repeat("0", 40)), "1", 40}, - {New(100, math.MaxInt32-1), "10", math.MaxInt32}, - {New(10, math.MaxInt32), "10", math.MaxInt32}, - {NewFromBigInt(pow10(40), math.MaxInt32-30), "10000000000", math.MaxInt32}, - } { - coef, exp := c.d.Coefficient().String(), c.d.Exponent() - got := c.d.Normalize() - if got.Coefficient().String() != c.coef || got.Exponent() != c.exp { - t.Errorf("Normalize(%se%d): got %se%d, want %se%d", coef, exp, got.Coefficient(), got.Exponent(), c.coef, c.exp) - } - if c.d.Coefficient().String() != coef || c.d.Exponent() != exp { - t.Errorf("Normalize(%se%d) changed its receiver to %se%d", coef, exp, c.d.Coefficient(), c.d.Exponent()) - } - } - - one := NewFromInt(1) - for _, equal := range [][]Decimal{ - {Decimal{}, Zero, New(0, 0), NewFromFloat(0), RequireFromString("0.00"), one.Sub(one), RequireFromString("0.4").Round(0), RequireFromString("0.5").RoundDown(0)}, - {New(1, 1), RequireFromString("10"), NewFromFloat(10), RequireFromString("10.00"), NewFromInt(20).Div(NewFromInt(2)), RequireFromString("2.5").Mul(NewFromInt(4)), New(10000000000, -9)}, - {New(-15, -1), RequireFromString("-1.500"), NewFromFloat(-1.5), NewFromInt(-3).Div(NewFromInt(2))}, - } { - want := equal[0] - for _, d := range equal[1:] { - if got := d.Normalize(); !reflect.DeepEqual(got, want) { - t.Errorf("Normalize(%se%d): got %#v, want %#v", d.Coefficient(), d.Exponent(), got, want) - } - } - } -} - func TestDecimal_Div(t *testing.T) { type Inp struct { a string @@ -2253,7 +1377,7 @@ func TestDecimal_QuoRem(t *testing.T) { t.Errorf("remainder too large: d=%v, d2= %v, prec=%d, q=%v, r=%v", d, d2, prec, q, r) } - if r.Sign()*d.Sign() < 0 { + if r.value.Sign()*d.value.Sign() < 0 { t.Errorf("signum of divisor and rest do not match: d=%v, d2= %v, prec=%d, q=%v, r=%v", d, d2, prec, q, r) } @@ -2318,7 +1442,7 @@ func TestDecimal_QuoRem2(t *testing.T) { d, d2, prec, q, r) } // rule 4: r and d have the same sign - if r.Sign()*d.Sign() < 0 { + if r.value.Sign()*d.value.Sign() < 0 { t.Errorf("signum of divisor and rest do not match, "+ "d=%v, d2=%v, prec=%d, q=%v, r=%v", d, d2, prec, q, r) @@ -2345,7 +1469,7 @@ func (d Decimal) DivOld(d2 Decimal, prec int) Decimal { } func sign(d Decimal) int { - return d.Sign() + return d.value.Sign() } // rules for rounded divide, rounded to integer @@ -2462,17 +1586,13 @@ func TestDecimal_RoundCash(t *testing.T) { {"3.93", 100, "4.00"}, {"393", 100, "393"}, } - defer func(p int) { DivisionPrecision = p }(DivisionPrecision) - for _, precision := range []int{16, 0} { - DivisionPrecision = precision - for i, test := range tests { - d, _ := NewFromString(test.d) - haveRounded := d.RoundCash(test.interval) - result, _ := NewFromString(test.result) + for i, test := range tests { + d, _ := NewFromString(test.d) + haveRounded := d.RoundCash(test.interval) + result, _ := NewFromString(test.result) - if !haveRounded.Equal(result) { - t.Errorf("Index %d, DivisionPrecision %d: Cash rounding for %q interval %d want %q, have %q", i, precision, test.d, test.interval, test.result, haveRounded) - } + if !haveRounded.Equal(result) { + t.Errorf("Index %d: Cash rounding for %q interval %d want %q, have %q", i, test.d, test.interval, test.result, haveRounded) } } } @@ -2503,20 +1623,14 @@ func TestDecimal_Mod(t *testing.T) { } inputs := map[Inp]string{ - Inp{"3", "2"}: "1", - Inp{"3451204593", "2454495034"}: "996709559", - Inp{"9999999999", "1275"}: "324", - Inp{"9999999999.9999998", "1275.49"}: "239.2399998", - Inp{"24544.95034", "0.3451204593"}: "0.3283950433", - Inp{"0.499999999999999999", "0.25"}: "0.249999999999999999", - Inp{"0.989512958912895912", "0.000001"}: "0.000000958912895912", - Inp{"0.1", "0.1"}: "0", - Inp{"0", "1.001"}: "0", - 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", + Inp{"3", "2"}: "1", + Inp{"3451204593", "2454495034"}: "996709559", + Inp{"24544.95034", ".3451204593"}: "0.3283950433", + Inp{".1", ".1"}: "0", + Inp{"0", "1.001"}: "0", + Inp{"-7.5", "2"}: "-1.5", + Inp{"7.5", "-2"}: "1.5", + Inp{"-7.5", "-2"}: "-1.5", } for inp, res := range inputs { @@ -2542,16 +1656,6 @@ func TestDecimal_Overflow(t *testing.T) { if !didPanic(func() { New(1, math.MaxInt32).Mul(New(1, math.MaxInt32)) }) { t.Fatalf("should have gotten an overflow panic") } - // -math.MinInt32 wraps back to itself as an int32. - if !didPanic(func() { New(1, math.MinInt32).QuoRem(New(1, 0), math.MinInt32) }) { - t.Fatalf("should have gotten an overflow panic") - } - if !didPanic(func() { New(1, math.MinInt32).DivRound(New(1, 0), math.MinInt32) }) { - t.Fatalf("should have gotten an overflow panic") - } - if q, _ := New(1, math.MaxInt32).QuoRem(New(1, 0), math.MinInt32+1); !q.Equal(New(1, math.MaxInt32)) { - t.Fatalf("precision math.MinInt32+1 should still work, got %s", q) - } } func TestDecimal_ExtremeValues(t *testing.T) { @@ -2673,36 +1777,6 @@ func TestBigFloat(t *testing.T) { } } -func TestBigFloatRoundTrip(t *testing.T) { - testCases := []string{ - "0", - "1", - "-1", - "0.5", - "-0.5", - "0.25", - "12.1", - "-12.1", - "123.456", - "-123.456", - } - - for _, s := range testCases { - d, err := NewFromString(s) - if err != nil { - t.Fatal(err) - } - bf := d.BigFloat() - roundTripD, err := NewFromBigFloat(bf) - if err != nil { - t.Fatalf("NewFromBigFloat failed for %s: %v", s, err) - } - if !d.Equal(roundTripD) { - t.Errorf("round trip failed: expected %s, got %s", d, roundTripD) - } - } -} - func TestDecimal_Min(t *testing.T) { // the first element in the array is the expected answer, rest are inputs testCases := [][]float64{ @@ -2758,109 +1832,108 @@ func TestDecimal_Max(t *testing.T) { } } -func scanHelper(t *testing.T, dbval interface{}, expected Decimal) { - t.Helper() - - 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 + // test the Scan method that implements the + // 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{} dbvalue := 54.33 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 // to handle these as well dbvalueFloat32 := float32(54.33) - expected = RequireFromString("54.33") - scanHelper(t, dbvalueFloat32, expected) + expected = NewFromFloat(float64(dbvalueFloat32)) + + 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 // and you specified a numeric format on the schema dbvalueInt := int64(0) expected = New(dbvalueInt, 0) - scanHelper(t, dbvalueInt, expected) - // also test uint64 - dbvalueUint64 := uint64(2) - expected = New(2, 0) - scanHelper(t, dbvalueUint64, expected) + err = a.Scan(dbvalueInt) + if err != nil { + // Scan failed... no need to test result value + t.Errorf("a.Scan(0) 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) + } + } // in case you specified a varchar in your SQL schema, - // the database driver may return either []byte or string + // the database driver will return byte slice []byte valueStr := "535.666" dbvalueStr := []byte(valueStr) - expected, err := NewFromString(valueStr) + expected, err = NewFromString(valueStr) if err != nil { t.Fatal(err) } - scanHelper(t, dbvalueStr, expected) - scanHelper(t, valueStr, expected) + + err = a.Scan(dbvalueStr) + 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{} - a := Decimal{} err = a.Scan(foo{}) if err == nil { t.Errorf("a.Scan(Foo{}) should have thrown an error but did not") } - - if err := a.Scan(nil); err == nil || !strings.Contains(err.Error(), "NullDecimal") { - t.Errorf("a.Scan(nil): got %v, want an error that points to NullDecimal", err) - } -} - -func TestDecimal_ScanTypes(t *testing.T) { - int128, _ := new(big.Int).SetString("170141183460469231731687303715884105727", 10) - for _, c := range []struct { - value interface{} - want string - }{ - {int(-3264), "-3264"}, - {int8(-7), "-7"}, - {int16(300), "300"}, - {int32(-70000), "-70000"}, - {int64(math.MinInt64), "-9223372036854775808"}, - {uint(3264), "3264"}, - {uint8(200), "200"}, - {uint16(65535), "65535"}, - {uint32(4000000000), "4000000000"}, - {uint64(math.MaxUint64), "18446744073709551615"}, - {float32(-0.1), "-0.1"}, - {float32(math.MaxFloat32), strconv.FormatFloat(math.MaxFloat32, 'f', -1, 32)}, - {float32(math.SmallestNonzeroFloat32), strconv.FormatFloat(math.SmallestNonzeroFloat32, 'f', -1, 32)}, - {*int128, "170141183460469231731687303715884105727"}, - {int128, "170141183460469231731687303715884105727"}, - {New(2, 3), "2000"}, - } { - var d Decimal - if err := d.Scan(c.value); err != nil || d.String() != c.want { - t.Errorf("Scan(%T %v): got %s, %v, want %s", c.value, c.value, d, err, c.want) - } - var n NullDecimal - if err := n.Scan(c.value); err != nil || !n.Valid || n.Decimal.String() != c.want { - t.Errorf("NullDecimal.Scan(%T %v): got %v, %v, want %s", c.value, c.value, n, err, c.want) - } - } - if int128.String() != "170141183460469231731687303715884105727" { - t.Errorf("Scan changed the scanned *big.Int to %s", int128) - } - - for _, v := range []interface{}{(*big.Int)(nil), math.NaN(), math.Inf(1), float32(math.Inf(-1))} { - var n NullDecimal - if err := n.Scan(v); err == nil || n.Valid { - t.Errorf("NullDecimal.Scan(%T %v): got %v, %v, want an error", v, v, n, err) - } - } } func TestDecimal_Value(t *testing.T) { @@ -2881,213 +1954,6 @@ func TestDecimal_Value(t *testing.T) { } } -func decodeSpannerHelper(t *testing.T, dbval interface{}, expected Decimal) { - t.Helper() - - a := Decimal{} - if err := a.DecodeSpanner(dbval); err != nil { - // DecodeSpanner failed... no need to test result value - t.Errorf("a.DecodeSpanner(%v) failed with message: %s", dbval, err) - } else if !a.Equal(expected) { - // DecodeSpanner succeeded... test resulting values - t.Errorf("%s does not equal to %s", a, expected) - } -} - -type spannerDecoder interface { - DecodeSpanner(input interface{}) error -} - -func TestDecimal_DecodeSpanner(t *testing.T) { - // test the DecodeSpanner method that implements spanner.Decoder interface - if _, ok := interface{}(new(Decimal)).(spannerDecoder); !ok { - t.Error("Decimal does not implement spanner.Decoder") - } - - dbvalue := 54.33 - expected := NewFromFloat(dbvalue) - decodeSpannerHelper(t, dbvalue, expected) - - // also test uint64 - dbvalueUint64 := uint64(2) - expected = New(2, 0) - decodeSpannerHelper(t, dbvalueUint64, expected) - - // ensure we can handle the return of either []byte or string - valueStr := "535.666" - dbvalueStr := []byte(valueStr) - expected, err := NewFromString(valueStr) - if err != nil { - t.Fatal(err) - } - decodeSpannerHelper(t, dbvalueStr, expected) - decodeSpannerHelper(t, valueStr, expected) - - type foo struct{} - a := Decimal{} - err = a.DecodeSpanner(foo{}) - if err == nil { - t.Errorf("a.DecodeSpanner(Foo{}) should have thrown an error but did not") - } -} - -type spannerEncoder interface { - EncodeSpanner() (interface{}, error) -} - -func TestDecimal_EncodeSpanner(t *testing.T) { - // Make sure this does implement the spanner.Encoder interface - if _, ok := interface{}(Decimal{}).(spannerEncoder); !ok { - t.Error("Decimal does not implement spanner.Encoder") - } - - // check that normal case is handled appropriately - a := New(1234, -2) - expected := "12.34" - value, err := a.Value() - if err != nil { - t.Errorf("Decimal(12.34).Value() failed with message: %s", err) - } else if got := value.(string); got != expected { - t.Errorf("%s does not equal to %s", a, expected) - } -} - -type bsonValueMarshaler interface { - MarshalBSONValue() (byte, []byte, error) -} - -type bsonValueUnmarshaler interface { - UnmarshalBSONValue(byte, []byte) error -} - -var ( - _ bsonValueMarshaler = Decimal{} - _ bsonValueMarshaler = NullDecimal{} - _ bsonValueUnmarshaler = (*Decimal)(nil) - _ bsonValueUnmarshaler = (*NullDecimal)(nil) -) - -// decimal128Tests are canonical values from the BSON spec's Decimal128 test corpus. -var decimal128Tests = []struct { - dec string - hex string // the 16 bytes as stored in BSON -}{ - {"0", "00000000000000000000000000004030"}, - {"1", "01000000000000000000000000004030"}, - {"-1", "010000000000000000000000000040b0"}, - {"0.1", "01000000000000000000000000003e30"}, - {"1E+3", "01000000000000000000000000004630"}, - {"1234567890123456789012345678901234", "f2af967ed05c82de3297ff6fde3c4030"}, - {"9.999999999999999999999999999999999E+6144", "ffffffff638e8d37c087adbe09edff5f"}, - {"1.000000000000000000000000000000000E+6144", "000000000a5bc138938d44c64d31fe5f"}, - {"1E-6176", "01000000000000000000000000000000"}, - {"-1E-6176", "01000000000000000000000000000080"}, - {"0E-6176", "00000000000000000000000000000000"}, - {"0E+6111", "0000000000000000000000000000fe5f"}, -} - -func mustDecodeHex(t *testing.T, s string) []byte { - t.Helper() - b, err := hex.DecodeString(s) - if err != nil { - t.Fatal(err) - } - return b -} - -func TestDecimal_MarshalBSONValue(t *testing.T) { - type test struct { - name string - d Decimal - hex string - } - tests := []test{ - {"zero value", Decimal{}, "00000000000000000000000000004030"}, - {"41 digits drop trailing zeros", RequireFromString("1" + strings.Repeat("0", 40)), "000000000a5bc138938d44c64d314e30"}, - {"exponent above 6111 gains trailing zeros", RequireFromString("1E+6144"), "000000000a5bc138938d44c64d31fe5f"}, - {"exponent below -6176 drops trailing zeros", New(1000, -6179), "01000000000000000000000000000000"}, - {"zero with the largest exponent", New(0, math.MaxInt32), "0000000000000000000000000000fe5f"}, - {"zero with the smallest exponent", New(0, math.MinInt32), "00000000000000000000000000000000"}, - } - for _, c := range decimal128Tests { - tests = append(tests, test{c.dec, RequireFromString(c.dec), c.hex}) - } - for _, c := range tests { - typ, data, err := c.d.MarshalBSONValue() - if err != nil || typ != bsonTypeDecimal128 || hex.EncodeToString(data) != c.hex { - t.Errorf("MarshalBSONValue(%s): got %#x, %x, %v, want %#x, %s", c.name, typ, data, err, bsonTypeDecimal128, c.hex) - } - } - - for _, c := range []struct { - name string - d Decimal - }{ - {"35 significant digits", RequireFromString("1.2345678901234567890123456789012345")}, - {"below the smallest Decimal128", RequireFromString("1E-6177")}, - {"above the largest Decimal128", RequireFromString("1E+6145")}, - {"smallest int32 exponent", New(1, math.MinInt32)}, - } { - if _, _, err := c.d.MarshalBSONValue(); err == nil { - t.Errorf("MarshalBSONValue(%s): got no error", c.name) - } - } -} - -func TestDecimal_UnmarshalBSONValue(t *testing.T) { - type test struct { - name string - typ byte - hex string - want string - } - tests := []test{ - {"string", bsonTypeString, "04000000312e3500", "1.5"}, - {"double", bsonTypeDouble, "0000000000000440", "2.5"}, - {"int32", bsonTypeInt32, "f9ffffff", "-7"}, - {"int64", bsonTypeInt64, "0000000000010000", "1099511627776"}, - {"null", bsonTypeNull, "", "0"}, - {"undefined", bsonTypeUndefined, "", "0"}, - {"Decimal128 with an implied coefficient above 10^34 - 1", bsonTypeDecimal128, "ffffffffffffffffffffffffffff116c", "0E+3"}, - {"Decimal128 with the coefficient 10^34", bsonTypeDecimal128, "00000000648e8d37c087adbe09ed4130", "0"}, - } - for _, c := range decimal128Tests { - tests = append(tests, test{c.dec, bsonTypeDecimal128, c.hex, c.dec}) - } - for _, c := range tests { - want := RequireFromString(c.want) - got := New(5, 0) - err := got.UnmarshalBSONValue(c.typ, mustDecodeHex(t, c.hex)) - if err != nil || !got.Equal(want) || got.Exponent() != want.Exponent() { - t.Errorf("UnmarshalBSONValue(%s): got %se%d, %v, want %se%d", c.name, got.Coefficient(), got.Exponent(), err, want.Coefficient(), want.Exponent()) - } - } - - for _, c := range []struct { - name string - typ byte - hex string - }{ - {"Decimal128 NaN", bsonTypeDecimal128, "0000000000000000000000000000007c"}, - {"Decimal128 -Infinity", bsonTypeDecimal128, "000000000000000000000000000000f8"}, - {"Decimal128 of 15 bytes", bsonTypeDecimal128, "000000000000000000000000004030"}, - {"double NaN", bsonTypeDouble, "000000000000f87f"}, - {"double +Infinity", bsonTypeDouble, "000000000000f07f"}, - {"string abc", bsonTypeString, "0400000061626300"}, - {"boolean", 0x08, "01"}, - } { - if err := new(Decimal).UnmarshalBSONValue(c.typ, mustDecodeHex(t, c.hex)); err == nil { - t.Errorf("UnmarshalBSONValue(%s): got no error", c.name) - } - } - - defer func(limit int) { MaxDecodeExponent = limit }(MaxDecodeExponent) - MaxDecodeExponent = 100 - if err := new(Decimal).UnmarshalBSONValue(bsonTypeDecimal128, mustDecodeHex(t, "01000000000000000000000000000000")); err == nil { - t.Error("UnmarshalBSONValue(1E-6176): got no error with MaxDecodeExponent 100") - } -} - // old tests after this line func TestDecimal_Scale(t *testing.T) { @@ -3192,963 +2058,21 @@ func TestDecimal_Cmp2(t *testing.T) { } } -func TestDecimal_Pow(t *testing.T) { - for _, testCase := range []struct { - Base string - Exponent string - Expected 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.0803715950193171"}, - {"962.0", "3.2791", "6055212360.0000044065516031"}, - {"5.69169126", "5.18515912", "8242.263447579484126"}, - {"13.1337", "3.5196719618391835", "8636.8562206447738448"}, - {"67762386.283696923", "4.85917691669163916681738", "112761146905370140621385730157437443321.9175573811731715"}, - {"-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.0000000000000139"}, - {"6696871.12", "-2.61313", "0.000000000000000001455825273411375"}, - {"-3.0", "-6.0", "0.0013717421124829"}, - {"-13.757", "-5.0", "-0.000002029463821"}, - {"-2", "-3", "-0.125"}, - {"0.5", "-20", "1048576"}, - {"2", "-17", "0.0000076293945313"}, - {"10", "-18", "0.000000000000000001"}, - {"100", "-17", "0.0000000000000000000000000000000001"}, - {"12345", "-5", "0.000000000000000000003487743424943423"}, - {"1000", "-6.5", "0.00000000000000000003162277660168379"}, - {"1.0041666666666667", "-360", "0.2238265956413493"}, - {"1.0041666666666667", "360.5", "4.4770424391814548"}, - {"0.01", "-7.585", "1479108388168207.4221993463468552"}, - {"-1.5", "-1001", "-5.403183104351711e-177"}, - {"-1.5", "-1000", "8.104774656527567e-177"}, - {"1.0000001", "-1000000000", "0.00000000000000000000000000000000000000000003720094576445977"}, - {"1.0000001", "1000000000.5", "26881038356701055136223322520735843418287670.4854123820834493"}, - {"100.0000000000000000", "0.5", "10"}, - {"4", "2.5", "32"}, - {"2", "1.1", "2.1435469250725863"}, - {"2", "0.5", "1.414213562373095"}, - {"1.28", "0.0833333333333333", "1.0207847284895002"}, - {"0.0208333333333333", "0.33", "0.2787342852450195"}, - {"1", "1" + strings.Repeat("0", 400) + ".5", "1"}, - } { - base := RequireFromString(testCase.Base) - exp := RequireFromString(testCase.Exponent) - expected := RequireFromString(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) - } - } - - // The expected exponent is beyond MaxDecodeExponent, so it can't be parsed from a string. - if got := New(15, -1).Pow(New(-100000, 0)); got.Cmp(New(7483321982518254, -17625)) != 0 { - t.Errorf("expected 7.483321982518254e-17610, got %s, for 1.5^-100000", got) +func TestPow(t *testing.T) { + a := New(4, 0) + b := New(2, 0) + x := a.Pow(b) + if x.String() != "16" { + t.Errorf("Error, saw %s", x.String()) } } -func TestDecimal_PowWithPrecision(t *testing.T) { - for _, testCase := range []struct { - Base string - Exponent string - Precision int32 - 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.08037159501931711868"}, - {"962.0", "3.2791", 15, "6055212360.000004406551603"}, - {"5.69169126", "5.18515912", 4, "8242.2634"}, - {"13.1337", "3.5196719618391835", 8, "8636.85622064"}, - {"67762386.283696923", "4.85917691669163916681738", 10, "112761146905370140621385730157437443321.9175573812"}, - {"-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.00000000000001385382"}, - {"6696871.12", "-2.61313", 24, "0.000000000000000001455825"}, - {"-3.0", "-6.0", 8, "0.00137174"}, - {"-13.757", "-5.0", 16, "-0.000002029463821"}, - {"10", "-18", 5, "0.000000000000000001"}, - {"5.63379", "-6.438", 2, "0.000015"}, - {"1000", "10.5", 5, "31622776601683793319988935444327.18534"}, - {"123.456", "7.89", 10, "31771028258180977.3090686597"}, - {"0.3", "-2.5", 16, "20.2860206483394857"}, - {"6.25", "0.5", 0, "3"}, - {"6.24" + strings.Repeat("9", 200), "0.5", 0, "2"}, - {"1.0041666666666667", "-360", -1, "0.2"}, - {"0.0625", "0.5", 1, "0.3"}, - {"0.00000625", "0.5", 1, "0.003"}, - {"2", "0.5", -1, "1"}, - {"123456", "0.5", -2, "400"}, - } { - 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"}, - {"10", -18, "0.000000000000000001"}, - {"12345", -5, "0.000000000000000000003487743424943423"}, - {"-1.5", -1001, "-5.403183104351711e-177"}, - } { - base := RequireFromString(testCase.Decimal) - expected := RequireFromString(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) - } - } - - // Exponents beyond MaxDecodeExponent can't be parsed, and String() on them is too slow for error messages. - for _, testCase := range []struct { - Decimal Decimal - Exponent int32 - Expected Decimal - }{ - {New(15, -1), math.MinInt32, New(4189793427303781, -378153115)}, - {New(3, 50000000), -1, New(3333333333333333, -50000016)}, - } { - result, err := testCase.Decimal.PowInt32(testCase.Exponent) - if err != nil || result.Cmp(testCase.Expected) != 0 { - t.Errorf("expected %de%d, got %de%d (err %v), for %de%d**%d", - testCase.Expected.Coefficient(), testCase.Expected.Exponent(), result.Coefficient(), result.Exponent(), err, - testCase.Decimal.Coefficient(), testCase.Decimal.Exponent(), testCase.Exponent) - } - } -} - -func TestDecimal_Pow_InexactResultExponent(t *testing.T) { - for _, testCase := range []struct { - Base string - Exponent string - }{ - {"3", "-6"}, - {"2", "0.5"}, - {"5.2", "6.3"}, - } { - result := RequireFromString(testCase.Base).Pow(RequireFromString(testCase.Exponent)) - if result.Exponent() != -int32(PowPrecisionNegativeExponent) { - t.Errorf("expected exponent %d, got %d, for %s^%s", -PowPrecisionNegativeExponent, result.Exponent(), testCase.Base, testCase.Exponent) - } - } -} - -func TestDecimal_Pow_RoundedToZeroResultExponent(t *testing.T) { - for _, testCase := range []struct { - Exponent int32 - Expected int32 - }{ - {-18, -33}, - {-1018, -1033}, - } { - result, _ := New(10, 0).PowInt32(testCase.Exponent) - if result.Exponent() != testCase.Expected { - t.Errorf("expected exponent %d, got %d, for 10**%d", testCase.Expected, result.Exponent(), testCase.Exponent) - } - } -} - -func TestDecimal_PowWithPrecision_HighPrecision(t *testing.T) { - two := New(2, 0) - for _, precision := range []int32{700, 3500} { - result, _ := two.PowWithPrecision(RequireFromString("0.5"), precision) - half := New(5, -precision-1) - lo, hi := result.Sub(half), result.Add(half) - if result.Exponent() != -precision || lo.Mul(lo).Cmp(two) >= 0 || hi.Mul(hi).Cmp(two) <= 0 { - t.Errorf("2^0.5 is not correctly rounded to %d places", precision) - } - } -} - -func TestDecimal_Pow_OutOfRange(t *testing.T) { - for _, testCase := range []struct { - Base string - Exponent string - }{ - {"10", "3000000000.5"}, - {"1.5", "-1000000000000"}, - {"0.5", "-4000000000.5"}, - {"0.1", "3000000000"}, - } { - base, exp := RequireFromString(testCase.Base), RequireFromString(testCase.Exponent) - - if _, err := base.PowWithPrecision(exp, 16); err == nil { - t.Errorf("expected out of range error for %s^%s", testCase.Base, testCase.Exponent) - } - if result := base.Pow(exp); !result.IsZero() { - t.Errorf("expected zero value for %s^%s, got %s", testCase.Base, testCase.Exponent, result) - } - } - - if _, err := RequireFromString("1.5").PowBigInt(big.NewInt(-1000000000000)); err == nil { - t.Errorf("expected out of range error for 1.5**-1000000000000") - } - if _, err := New(1, -2).PowInt32(1<<30 + 1); err == nil { - t.Errorf("expected out of range error for 0.01**1073741825") - } -} - -func TestDecimal_PowInt32_ZeroNegativeExponent(t *testing.T) { - if _, err := Zero.PowInt32(-2); err == nil { - t.Errorf("expected error, cannot represent infinity value of 0 ** y, where y < 0") - } - if _, err := Zero.PowBigInt(big.NewInt(-2)); err == nil { - t.Errorf("expected error, cannot represent infinity value of 0 ** y, where y < 0") - } -} - -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"}, - {"10", big.NewInt(-18), "0.000000000000000001"}, - {"12345", big.NewInt(-5), "0.000000000000000000003487743424943423"}, - {"-1.5", big.NewInt(-1001), "-5.403183104351711e-177"}, - {"1.0000000000000000000000000000001", big.NewInt(-100000000000000000), "0.99999999999999"}, - {"-1", new(big.Int).Neg(new(big.Int).Exp(big.NewInt(10), big.NewInt(30), nil)), "1"}, - } { - 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") - } -} - -func TestDecimal_SqrtRound(t *testing.T) { - for _, c := range []struct { - in string - places int32 - want string - }{ - {"0", 16, "0"}, - {"1", 16, "1"}, - {"4", 16, "2"}, - {"1.44", 16, "1.2"}, - {"3", 16, "1.7320508075688773"}, - {"4.5", 16, "2.1213203435596426"}, - {"0.002342", 16, "0.0483942145302514"}, - {"0.002342", 4, "0.0484"}, - {"0.002342", 32, "0.04839421453025144409653399922499"}, - {"3289854", 16, "1813.7954680724064484"}, - {"0.0025", 1, "0.1"}, - {"12345", -1, "110"}, - {"1e40", 0, "1e20"}, - {"1e-40", 16, "0"}, - {"1e-9000", 16, "0"}, - } { - got, err := RequireFromString(c.in).SqrtRound(c.places) - if want := RequireFromString(c.want); err != nil || !got.Equal(want) || got.Exponent() != -c.places { - t.Errorf("SqrtRound(%s, %d): got %s (exponent %d), %v, want %s", c.in, c.places, got, got.Exponent(), err, want) - } - } - - if got, err := New(1, math.MinInt32).SqrtRound(16); err != nil || !got.IsZero() { - t.Errorf("SqrtRound(1e%d, 16): got %s, %v, want 0", math.MinInt32, got, err) - } - for _, d := range []Decimal{NewFromInt(-1), RequireFromString("-0.0001")} { - if _, err := d.SqrtRound(16); err == nil { - t.Errorf("SqrtRound(%s, 16): got no error", d) - } - } - - // r is the correctly rounded root of d exactly when (r - u/2)^2 <= d < (r + u/2)^2, with u = 10^-places - r := rand.New(rand.NewSource(1)) - for i := 0; i < 2000; i++ { - d := NewFromBigInt(new(big.Int).Rand(r, pow10(int64(1+r.Intn(40)))), int32(r.Intn(80)-50)) - places := int32(r.Intn(46) - 5) - got, err := d.SqrtRound(places) - half := New(5, -places-1) - lo, hi := got.Sub(half), got.Add(half) - if err != nil || got.Sign() > 0 && lo.Mul(lo).GreaterThan(d) || hi.Mul(hi).LessThanOrEqual(d) { - t.Fatalf("SqrtRound(%s, %d): got %s, %v, which isn't the correctly rounded root", d, places, got, err) - } - } -} - -func TestDecimal_Sqrt(t *testing.T) { - if got, err := NewFromInt(2).Sqrt(); err != nil || got.String() != "1.414213562373095" || got.Exponent() != -16 { - t.Errorf("Sqrt(2): got %s (exponent %d), %v", got, got.Exponent(), err) - } - - defer func(prec int) { DivisionPrecision = prec }(DivisionPrecision) - DivisionPrecision = 4 - if got, err := NewFromInt(2).Sqrt(); err != nil || got.String() != "1.4142" { - t.Errorf("Sqrt(2) with DivisionPrecision 4: got %s, %v", got, err) - } - if _, err := NewFromInt(-4).Sqrt(); err == nil { - t.Error("Sqrt(-4): got no error") - } -} - -func TestDecimal_RootRound(t *testing.T) { - for _, c := range []struct { - in string - n int64 - places int32 - want string - }{ - {"27", 3, 16, "3"}, - {"2", 3, 16, "1.2599210498948732"}, - {"-8", 3, 16, "-2"}, - {"-2", 3, 5, "-1.25992"}, - {"16", 4, 16, "2"}, - {"32", 5, 0, "2"}, - {"0", 5, 16, "0"}, - {"1e-30", 3, 16, "0.0000000001"}, - {"0.001", 3, 2, "0.1"}, - {"0.125", 3, 0, "1"}, - {"-0.125", 3, 0, "-1"}, - {"1000000", 3, -1, "100"}, - {"2", 1, 3, "2"}, - {"5e-150", 10, 14, "0"}, - } { - got, err := RequireFromString(c.in).RootRound(c.n, c.places) - if want := RequireFromString(c.want); err != nil || !got.Equal(want) || got.Exponent() != -c.places { - t.Errorf("RootRound(%s, %d, %d): got %s (exponent %d), %v, want %s", c.in, c.n, c.places, got, got.Exponent(), err, want) - } - } - - for _, c := range []struct { - d Decimal - n int64 - }{{NewFromInt(2), 0}, {NewFromInt(2), -3}, {NewFromInt(-4), 2}, {NewFromInt(-16), 4}} { - if _, err := c.d.RootRound(c.n, 16); err == nil { - t.Errorf("RootRound(%s, %d, 16): got no error", c.d, c.n) - } - } - - // r is the correctly rounded root of d exactly when (r - u/2)^n <= d < (r + u/2)^n, with u = 10^-places - r := rand.New(rand.NewSource(1)) - for i := 0; i < 2000; i++ { - d := NewFromBigInt(new(big.Int).Rand(r, pow10(int64(1+r.Intn(30)))), int32(r.Intn(60)-40)) - n := int64(1 + r.Intn(7)) - places := int32(r.Intn(33) - 2) - got, err := d.RootRound(n, places) - half := New(5, -places-1) - lo, _ := got.Sub(half).PowInt32(int32(n)) - hi, _ := got.Add(half).PowInt32(int32(n)) - if err != nil || got.Sign() > 0 && lo.GreaterThan(d) || hi.LessThanOrEqual(d) { - t.Fatalf("RootRound(%s, %d, %d): got %s, %v, which isn't the correctly rounded root", d, n, places, got, err) - } - if sqrt, _ := d.SqrtRound(places); n == 2 && !got.Equal(sqrt) { - t.Fatalf("RootRound(%s, 2, %d) = %s, SqrtRound gives %s", d, places, got, sqrt) - } - } -} - -func TestDecimal_Root(t *testing.T) { - if got, err := NewFromInt(2).Root(3); err != nil || got.String() != "1.2599210498948732" || got.Exponent() != -16 { - t.Errorf("Root(2, 3): got %s (exponent %d), %v", got, got.Exponent(), err) - } - - defer func(prec int) { DivisionPrecision = prec }(DivisionPrecision) - DivisionPrecision = 4 - if got, err := NewFromInt(2).Root(3); err != nil || got.String() != "1.2599" { - t.Errorf("Root(2, 3) with DivisionPrecision 4: got %s, %v", got, err) - } -} - -func TestDecimal_IsInteger(t *testing.T) { - for _, testCase := range []struct { - Dec string - IsInteger bool - }{ - {"0", true}, - {"0.0000", true}, - {"0.01", false}, - {"0.01010101010000", false}, - {"12.0", true}, - {"12.00000000000000", true}, - {"12.10000", false}, - {"9999.0000", true}, - {"99999999.000000000", true}, - {"-656323444.0000000000000", true}, - {"-32768.01234", false}, - {"-32768.0123423562623600000", false}, - } { - d, err := NewFromString(testCase.Dec) - if err != nil { - t.Fatal(err) - } - if d.IsInteger() != testCase.IsInteger { - t.Errorf("expect %t, got %t, for %s", testCase.IsInteger, d.IsInteger(), testCase.Dec) - } - } -} - -func TestDecimal_ExpHullAbrham(t *testing.T) { - for _, testCase := range []struct { - Dec string - OverallPrecision uint32 - ExpectedDec string - }{ - {"0.000001", 4, "1"}, - {"-0.000001", 4, "1"}, - {"0.00000100", 4, "1"}, - {"-0.00000100", 4, "1"}, - {"0.00009", 4, "1"}, - {"0.000000000001", 8, "1"}, - {"-0.000000000001", 8, "1"}, - {"0.001", 4, "1.001"}, - {"-0.001", 4, "0.999"}, - {"0", 1, "1"}, - {"0.00", 5, "1"}, - {"0.5", 5, "1.6487"}, - {"0.569297", 10, "1.767024397"}, - {"0.569297", 16, "1.76702439654095"}, - {"0.569297", 20, "1.7670243965409496521"}, - {"1.0", 0, "3"}, - {"1.0", 1, "3"}, - {"1.0", 5, "2.7183"}, - {"1.0", 10, "2.718281828"}, - {"3.0", 0, "20"}, - {"3.0", 2, "20"}, - {"5.26", 0, "200"}, - {"5.26", 2, "190"}, - {"5.26", 10, "192.4814913"}, - {"5.2663117716", 2, "190"}, - {"5.2663117716", 10, "193.7002327"}, - {"26.1", 2, "220000000000"}, - {"26.1", 10, "216314672100"}, - {"26.1", 20, "216314672147.05767284"}, - {"50.1591", 10, "6078834887000000000000"}, - {"50.1591", 30, "6078834886623434464595.07937141"}, - {"-0.5", 5, "0.60653"}, - {"-0.569297", 10, "0.5659231429"}, - {"-0.569297", 16, "0.565923142859576"}, - {"-0.569297", 20, "0.56592314285957604443"}, - {"-1.0", 1, "0.4"}, - {"-1.0", 5, "0.36788"}, - {"-3.0", 1, "0.05"}, - {"-3.0", 2, "0.05"}, - {"-3.0", 10, "0.04978706837"}, - {"-5.26", 2, "0.0052"}, - {"-5.26", 10, "0.005195304719"}, - {"-5.2663117716", 2, "0.0052"}, - {"-5.2663117716", 10, "0.005162616411"}, - {"-26.1", 2, "0.0000000000046"}, - {"-26.1", 15, "0.00000000000462289492466867"}, - {"-50.1591", 10, "0.0000000000000000000001645052084"}, - {"-50.1591", 30, "0.000000000000000000000164505208424152908216198963722"}, - {"23.31", 1, "10000000000"}, - {"50", 2, "5200000000000000000000"}, - {"-50", 2, "0.00000000000000000000019"}, - } { - d, _ := NewFromString(testCase.Dec) - expected, _ := NewFromString(testCase.ExpectedDec) - - exp, err := d.ExpHullAbrham(testCase.OverallPrecision) - if err != nil { - t.Fatal(err) - } - - if exp.Cmp(expected) != 0 { - t.Errorf("expected %s, got %s, for decimal %s", testCase.ExpectedDec, exp.String(), testCase.Dec) - } - - } -} - -// ExpHullAbrham is documented to return overallPrecision significant figures. -// It used to round the result to overallPrecision decimal places whenever the -// result was < 1, discarding the leading-zero positions, so every sufficiently -// negative argument lost significant figures (exp(-50).ExpHullAbrham(10) -// returned 0). Check the delivered precision against exp(x) computed to 40 -// digits with mpmath, across the negative half-line. -func TestDecimal_ExpHullAbrham_significantFigures(t *testing.T) { - trueExp := map[string]string{ - "-2.5": "8.20849986238987951695286744671598078378041e-2", - "-5": "6.73794699908546709663604842314842424884959e-3", - "-10": "4.53999297624848515355915155605506102379181e-5", - "-20": "2.06115362243855782796594038015582097637581e-9", - "-30": "9.35762296884017460491583222337870674495832e-14", - "-50": "1.92874984796391778301734281652701257475283e-22", - } - for _, x := range []string{"-2.5", "-5", "-10", "-20", "-30", "-50"} { - d := RequireFromString(x) - want := RequireFromString(trueExp[x]) - for _, prec := range []uint32{10, 20, 30} { - got, err := d.ExpHullAbrham(prec) - if err != nil { - t.Fatalf("exp(%s).ExpHullAbrham(%d): %v", x, prec, err) - } - if got.IsZero() { - t.Errorf("exp(%s).ExpHullAbrham(%d) = 0, want ~%s", x, prec, want) - continue - } - relErr := got.Sub(want).Abs().DivRound(want, 45) - tol := New(1, -int32(prec)+1) // one unit in the prec-th significant figure - if relErr.Cmp(tol) > 0 { - t.Errorf("exp(%s).ExpHullAbrham(%d) = %s: relative error %s > %s, fewer than %d significant figures", - x, prec, got, relErr, tol, prec) - } - } - } -} - -func TestDecimal_ExpTaylor(t *testing.T) { - for _, testCase := range []struct { - Dec string - Precision int32 - ExpectedDec string - }{ - {"0", 1, "1"}, - {"0.00", 5, "1"}, - {"0", -1, "0"}, - {"0.5", 5, "1.64872"}, - {"0.569297", 10, "1.7670243965"}, - {"0.569297", 16, "1.7670243965409497"}, - {"0.569297", 20, "1.76702439654094965215"}, - {"1.0", 0, "3"}, - {"1.0", 1, "2.7"}, - {"1.0", 5, "2.71828"}, - {"1.0", -1, "0"}, - {"1.0", -5, "0"}, - {"3.0", 1, "20.1"}, - {"3.0", 2, "20.09"}, - {"5.26", 0, "192"}, - {"5.26", 2, "192.48"}, - {"5.26", 10, "192.4814912972"}, - {"5.26", -2, "200"}, - {"5.2663117716", 2, "193.70"}, - {"5.2663117716", 10, "193.7002326701"}, - {"26.1", 2, "216314672147.06"}, - {"26.1", 20, "216314672147.05767284062928674083"}, - {"26.1", -2, "216314672100"}, - {"26.1", -10, "220000000000"}, - {"50.1591", 10, "6078834886623434464595.0793714061"}, - {"-0.5", 5, "0.60653"}, - {"-0.569297", 10, "0.5659231429"}, - {"-0.569297", 16, "0.565923142859576"}, - {"-0.569297", 20, "0.56592314285957604443"}, - {"-1.0", 1, "0.4"}, - {"-1.0", 5, "0.36788"}, - {"-1.0", -1, "0"}, - {"-1.0", -5, "0"}, - {"-3.0", 1, "0.1"}, - {"-3.0", 2, "0.05"}, - {"-3.0", 10, "0.0497870684"}, - {"-5.26", 2, "0.01"}, - {"-5.26", 10, "0.0051953047"}, - {"-5.26", -2, "0"}, - {"-5.2663117716", 2, "0.01"}, - {"-5.2663117716", 10, "0.0051626164"}, - {"-26.1", 2, "0"}, - {"-26.1", 15, "0.000000000004623"}, - {"-26.1", -2, "0"}, - {"-26.1", -10, "0"}, - {"-50.1591", 10, "0"}, - {"-50.1591", 30, "0.000000000000000000000164505208"}, - } { - d, _ := NewFromString(testCase.Dec) - expected, _ := NewFromString(testCase.ExpectedDec) - - exp, err := d.ExpTaylor(testCase.Precision) - if err != nil { - t.Fatal(err) - } - - if exp.Cmp(expected) != 0 { - t.Errorf("expected %s, got %s", testCase.ExpectedDec, exp.String()) - } - } -} - -// TestDecimal_ExpTaylor_Concurrency tests the concurrency safety of the -// ExpTaylor method, particularly focusing on the shared state caching mechanism -// for factorial calculations. -func TestDecimal_ExpTaylor_Concurrency(t *testing.T) { - resetFactorials := func() { - factorialsMutex.Lock() - factorials = []Decimal{New(1, 0)} - factorialsMutex.Unlock() - } - t.Run("concurrent calculation works as expected", func(t *testing.T) { - resetFactorials() - - // Run multiple goroutines calculating exp concurrently - // This should trigger concurrent factorial calculations. - var wg sync.WaitGroup - numGoroutines := 50 - results := make([]Decimal, numGoroutines) - errors := make([]error, numGoroutines) - - for i := 0; i < numGoroutines; i++ { - wg.Add(1) - go func(index int) { - defer wg.Done() - d := NewFromFloat(5.0) - result, err := d.ExpTaylor(10) - results[index] = result - errors[index] = err - }(i) - } - - wg.Wait() - - // Check all goroutines succeeded. - for i, err := range errors { - if err != nil { - t.Errorf("goroutine %d: ExpTaylor failed: %v", i, err) - } - } - - // All results should be identical. - expected := results[0] - for i, result := range results[1:] { - if !result.Equal(expected) { - t.Errorf("goroutine %d: result mismatch: expected %s, got %s", i+1, expected.String(), result.String()) - } - } - - // Verify factorials slice doesn't have duplicate Zero values - // (which would indicate the double-check locking failed). - factorialsMutex.RLock() - for i, f := range factorials { - if i > 0 && f.IsZero() { - t.Errorf("factorial at index %d is Zero, double-check locking may have failed", i) - } - } - factorialsMutex.RUnlock() - }) - - t.Run("race detection", func(t *testing.T) { - // NOTE: Factorials from previous subtests are intentionally reused here. - // This tests that concurrent access works correctly with a pre-populated cache, - // which is the real-world scenario. Run with `go test -race`. - - var wg sync.WaitGroup - numGoroutines := 100 - - for i := 0; i < numGoroutines; i++ { - wg.Add(1) - go func(val float64) { - defer wg.Done() - d := NewFromFloat(val) - _, err := d.ExpTaylor(10) - if err != nil { - t.Errorf("ExpTaylor failed for value %f: %v", val, err) - } - }(float64(i%10) + 0.5) - } - - wg.Wait() - }) -} - -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"}, - {"10", 5, "2.30259"}, - // e^2.5 truncated, ln is 2.5 - 5e-45, so the first working precisions can't decide the rounding - {"12.18249396070347343807017595116796618318276779", 0, "2"}, - {"1e-28", 16, "-64.4723826038332792"}, - {"1e-34", 13, "-78.2878931617976"}, - {"839101.0351", 25, "13.6400864014410013994397240"}, - {"839101.0351094726488848490572028502", 50, "13.64008640145229044389152437468283605382056561604272"}, - {"5023583755703750094849.03519358513093500275017501750602739169823", 25, "49.9684305274348922267409953"}, - {"5023583755703750094849.03519358513093500275017501750602739169823", -1, "50.0"}, - {"66.12", 18, "4.191471272952823429"}, - } { - 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) - } - } -} - -// ln(10^k) is k*ln(10), and strLn10 has far more digits than any rounding below needs. -func TestDecimal_LnPowersOfTen(t *testing.T) { - ln10 := RequireFromString(strLn10) - for k := int32(-40); k <= 40; k++ { - for p := int32(0); p <= 30; p++ { - want := ln10.Mul(New(int64(k), 0)).Round(p) - if got, err := New(1, k).Ln(p); err != nil || !got.Equal(want) { - t.Errorf("Ln(1e%d, %d): got %s, %v, want %s", k, p, got, err, want) - } - } - } -} - -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) { - for _, testCase := range []struct { - Dec string - ExpectedNumDigits int - }{ - {"0", 1}, - {"0.00", 1}, - {"1.0", 2}, - {"3.0", 2}, - {"5.26", 3}, - {"5.2663117716", 11}, - {"3164836416948884.2162426426426267863", 35}, - {"26.1", 3}, - {"529.1591", 7}, - {"-1.0", 2}, - {"-3.0", 2}, - {"-5.26", 3}, - {"-5.2663117716", 11}, - {"-26.1", 3}, - {"", 1}, - // Regression for #420: float64 math.Log10 rounds 1eN down to - // N-1 for some N (e.g. log10(1e15) == 14.999...), so the old - // fast path under-reported by one for exact-power coefficients. - {"1000000000000000", 16}, - {"-1000000000000000", 16}, - {"1000000000000000E20", 16}, - {"9007199254740992", 16}, // 1<<53, the old fast-path boundary - {"-9007199254740992", 16}, - } { - d, _ := NewFromString(testCase.Dec) - - nums := d.NumDigits() - if nums != testCase.ExpectedNumDigits { - t.Errorf("expected %d digits for decimal %s, got %d", testCase.ExpectedNumDigits, testCase.Dec, nums) - } - } - - // every power of ten and of two in int64, and their neighbours - values := []int64{math.MaxInt64, math.MinInt64} - for p := int64(1); p <= math.MaxInt64/10; p *= 10 { - values = append(values, p-1, p, p+1, -p+1, -p, -p-1) - } - for j := uint(0); j < 63; j++ { - p := int64(1) << j - values = append(values, p-1, p, p+1, -p+1, -p, -p-1) - } - for _, v := range values { - want := len(strconv.FormatInt(v, 10)) - if v < 0 { - want-- - } - if got := New(v, 0).NumDigits(); got != want { - t.Errorf("expected %d digits for %d, got %d", want, v, got) - } - } -} - -func TestDecimal_Float64MatchesRat(t *testing.T) { - // coefficients and exponents in the range of the Float64 fast path - rng := rand.New(rand.NewSource(7)) - for i := 0; i < 100000; i++ { - v := rng.Int63n(1<<54+1) - 1<<53 - if i%3 == 0 { - v /= 1e10 // shorter coefficients, so more exact results - } - d := New(v, -int32(rng.Intn(23))) - f, exact := d.Float64() - wantF, wantExact := d.Rat().Float64() - if v == 0 { - wantF, wantExact = 0, true - } - if math.Float64bits(f) != math.Float64bits(wantF) || exact != wantExact { - t.Fatalf("%s.Float64() = %v, %v; want %v, %v", d, f, exact, wantF, wantExact) - } +func TestNegativePow(t *testing.T) { + a := New(4, 0) + b := New(-2, 0) + x := a.Pow(b) + if x.String() != "0.0625" { + t.Errorf("Error, saw %s", x.String()) } } @@ -4199,36 +2123,6 @@ func TestDecimal_Coefficient(t *testing.T) { } } -func TestDecimal_CoefficientInt64(t *testing.T) { - type Inp struct { - Dec string - Coefficient int64 - } - - testCases := []Inp{ - {"1", 1}, - {"1.111", 1111}, - {"1.000000", 1000000}, - {"1.121215125511", 1121215125511}, - {"100000000000000000", 100000000000000000}, - {"9223372036854775807", 9223372036854775807}, - {"10000000000000000000", -8446744073709551616}, // undefined value - } - - // add negative cases - for _, tc := range testCases { - testCases = append(testCases, Inp{"-" + tc.Dec, -tc.Coefficient}) - } - - for _, tc := range testCases { - d := RequireFromString(tc.Dec) - coefficient := d.CoefficientInt64() - if coefficient != tc.Coefficient { - t.Errorf("expect coefficient %d, got %d, for decimal %s", tc.Coefficient, coefficient, tc.Dec) - } - } -} - func TestNullDecimal_Scan(t *testing.T) { // test the Scan method that implements the // sql.Scanner interface @@ -4357,135 +2251,6 @@ func TestNullDecimal_Value(t *testing.T) { } } -func TestNullDecimal_DecodeSpanner(t *testing.T) { - // test the DecodeSpanner method that implements the - // spanner.Decoder interface - if _, ok := interface{}(new(NullDecimal)).(spannerDecoder); !ok { - t.Error("NullDecimal does not implement spanner.Decoder") - } - - // Make sure handles nil value - a := NullDecimal{} - var dbvaluePtr interface{} - err := a.DecodeSpanner(dbvaluePtr) - if err != nil { - // DecodeSpanner failed... no need to test result value - t.Errorf("a.DecodeSpanner(nil) failed with message: %s", err) - } else { - if a.Valid { - t.Errorf("%s is not null", a.Decimal) - } - } - - // Make sure handles nil *string - dbvaluePtr = (*string)(nil) - if err := a.DecodeSpanner(dbvaluePtr); err != nil { - // DecodeSpanner failed... no need to test result value - t.Errorf("a.DecodeSpanner((*string)(nil)) failed with message: %s", err) - } else { - if a.Valid { - t.Errorf("%s is not null", a.Decimal) - } - } - - valueStr := "535.666" - expected, err := NewFromString(valueStr) - if err != nil { - t.Fatal(err) - } - - // Handle string - err = a.DecodeSpanner(valueStr) - if err != nil { - // DecodeSpanner failed... no need to test result value - t.Errorf("a.DecodeSpanner('535.666') failed with message: %s", err) - } else { - // DecodeSpanner succeeded... test resulting values - if !a.Valid { - t.Errorf("%s is null", a.Decimal) - } else if !a.Decimal.Equals(expected) { - t.Errorf("%v does not equal %v", a, expected) - } - } - - // handle *string - err = a.DecodeSpanner(&valueStr) - if err != nil { - // DecodeSpanner failed... no need to test result value - t.Errorf("a.DecodeSpanner('535.666') failed with message: %s", err) - } else { - // DecodeSpanner succeeded... test resulting values - if !a.Valid { - t.Errorf("%s is null", a.Decimal) - } else if !a.Decimal.Equals(expected) { - t.Errorf("%v does not equal %v", a, expected) - } - } -} - -func TestNullDecimal_EncodeSpanner(t *testing.T) { - // Make sure this does implement the spanner.Encoder interface - var nullDecimal NullDecimal - if _, ok := interface{}(nullDecimal).(spannerEncoder); !ok { - t.Error("NullDecimal does not implement spanner.Encoder") - } - - // check that null is handled appropriately - value, err := nullDecimal.EncodeSpanner() - if err != nil { - t.Errorf("NullDecimal{}.Valid() failed with message: %s", err) - } else if value != nil { - t.Errorf("%v is not nil", value) - } - - // check that normal case is handled appropriately - a := NullDecimal{Decimal: New(1234, -2), Valid: true} - expected := "12.34" - value, err = a.EncodeSpanner() - if err != nil { - t.Errorf("NullDecimal(12.34).EncodeSpanner() failed with message: %s", err) - } else if value.(string) != expected { - t.Errorf("%v does not equal %v", a, expected) - } -} - -func TestNullDecimal_MarshalBSONValue(t *testing.T) { - for _, c := range []struct { - d NullDecimal - typ byte - hex string - }{ - {NullDecimal{}, bsonTypeNull, ""}, - {NewNullDecimal(New(1, 3)), bsonTypeDecimal128, "01000000000000000000000000004630"}, - } { - typ, data, err := c.d.MarshalBSONValue() - if err != nil || typ != c.typ || hex.EncodeToString(data) != c.hex { - t.Errorf("MarshalBSONValue(%v): got %#x, %x, %v, want %#x, %s", c.d, typ, data, err, c.typ, c.hex) - } - } -} - -func TestNullDecimal_UnmarshalBSONValue(t *testing.T) { - for _, c := range []struct { - name string - typ byte - hex string - want string // "" if the result is invalid - wantErr bool - }{ - {"Decimal128", bsonTypeDecimal128, "01000000000000000000000000004630", "1000", false}, - {"null", bsonTypeNull, "", "", false}, - {"undefined", bsonTypeUndefined, "", "", false}, - {"boolean", 0x08, "01", "", true}, - } { - got := NewNullDecimal(New(5, 0)) - err := got.UnmarshalBSONValue(c.typ, mustDecodeHex(t, c.hex)) - if (err != nil) != c.wantErr || got.Valid != (c.want != "") || got.Valid && got.Decimal.String() != c.want { - t.Errorf("UnmarshalBSONValue(%s): got %v, %v, want %q and error=%v", c.name, got, err, c.want, c.wantErr) - } - } -} - func TestBinary(t *testing.T) { for _, y := range testTable { x := y.float @@ -4513,43 +2278,6 @@ func TestBinary(t *testing.T) { } } -func TestBinary_Zero(t *testing.T) { - var d1 Decimal - - b, err := d1.MarshalBinary() - if err != nil { - t.Fatalf("error marshalling %v to binary: %v", d1, err) - } - - var d2 Decimal - err = (&d2).UnmarshalBinary(b) - if err != nil { - t.Errorf("error unmarshalling from binary: %v", err) - } - - if !d1.Equals(d2) { - t.Errorf("expected %v when restoring, got %v", d1, d2) - } -} - -func TestBinary_DataTooShort(t *testing.T) { - var d Decimal - - err := d.UnmarshalBinary(nil) // nil slice has length 0 - if err == nil { - t.Fatalf("expected error, got %v", d) - } -} - -func TestBinary_InvalidValue(t *testing.T) { - var d Decimal - - err := d.UnmarshalBinary([]byte{0, 0, 0, 0, 'x'}) // valid exponent, invalid value - if err == nil { - t.Fatalf("expected error, got %v", d) - } -} - func slicesEqual(a, b []byte) bool { for i, val := range a { if b[i] != val { @@ -4616,17 +2344,6 @@ func TestSum(t *testing.T) { if !sum.Equal(New(45, 0)) { t.Errorf("Failed to calculate sum, expected %s got %s", New(45, 0), sum) } - - // reflect.DeepEqual, used by testify's assert.Equal, also compares the big.Int internals - if s := Sum(New(5, -1), New(-5, -1), Zero); !reflect.DeepEqual(s, New(0, -1)) { - t.Errorf("Sum(0.5, -0.5, 0) = %#v, want the same representation as New(0, -1)", s) - } - if s := Zero.Shift(2); !reflect.DeepEqual(s, New(0, 2)) { - t.Errorf("Zero.Shift(2) = %#v, want the same representation as New(0, 2)", s) - } - if s := New(7, 0).Sub(New(7, 0)).Shift(2); !reflect.DeepEqual(s, New(0, 2)) { - t.Errorf("(7 - 7).Shift(2) = %#v, want the same representation as New(0, 2)", s) - } } func TestAvg(t *testing.T) { @@ -4826,403 +2543,22 @@ func TestTan(t *testing.T) { } } -func TestNewNullDecimal(t *testing.T) { - d := NewFromInt(1) - nd := NewNullDecimal(d) - - if !nd.Valid { - t.Errorf("expected NullDecimal to be valid") - } - if nd.Decimal != d { - t.Errorf("expected NullDecimal to hold the provided Decimal") - } -} - -func TestDecimal_String(t *testing.T) { - type testData struct { - input string - expected string - } - - tests := []testData{ - {"1.22", "1.22"}, - {"1.00", "1"}, - {"153.192", "153.192"}, - {"999.999", "999.999"}, - {"0.0000000001", "0.0000000001"}, - {"0.0000000000", "0"}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } else if d.String() != test.expected { - t.Errorf("expected %s, got %s", test.expected, d.String()) - } - } -} - -func TestDecimal_StringWithTrailing(t *testing.T) { - type testData struct { - input string - expected string - } - - defer func() { - TrimTrailingZeros = true - }() - - TrimTrailingZeros = false - tests := []testData{ - {"1.00", "1.00"}, - {"0.00", "0.00"}, - {"129.123000", "129.123000"}, - {"1.0000E3", "1000.0"}, // 1000 to the nearest tenth - {"10000E-1", "1000.0"}, // 1000 to the nearest tenth - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } else if d.String() != test.expected { - x := d.String() - fmt.Println(x) - t.Errorf("expected %s, got %s", test.expected, d.String()) - } - } -} - -func TestDecimal_StringWithScientificNotationWhenNeeded(t *testing.T) { - type testData struct { - input string - expected string - } - - defer func() { - UseScientificNotation = false - }() - UseScientificNotation = true - - tests := []testData{ - {"1.0E3", "1.0E3"}, // 1000 to the nearest hundred - {"1.00E3", "1.00E3"}, // 1000 to the nearest ten - {"1.000E3", "1000"}, // 1000 to the nearest one - {"1E3", "1E3"}, // 1000 to the nearest thousand - {"-1E3", "-1E3"}, // -1000 to the nearest thousand - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } else if d.String() != test.expected { - x := d.String() - fmt.Println(x) - t.Errorf("expected %s, got %s", test.expected, d.String()) - } - } -} - -func TestDecimal_Format(t *testing.T) { - pi := RequireFromString("3.14159") - for i, c := range []struct { - format string - v interface{} - want string - }{ - {"%v", pi, "3.14159"}, - {"%s", pi, "3.14159"}, - {"%v", Decimal{}, "0"}, - {"%10v", pi, " 3.14159"}, - {"%-10s|", pi, "3.14159 |"}, - {"%.3s", pi, "3.1"}, - {"%q", pi, `"3.14159"`}, - {"%x", pi, "332e3134313539"}, - {"%v", &pi, "3.14159"}, - {"%v", (*Decimal)(nil), ""}, - {"%v", []Decimal{pi, pi.Neg()}, "[3.14159 -3.14159]"}, - {"%+v", NewNullDecimal(pi), "{Decimal:3.14159 Valid:true}"}, - - {"%f", RequireFromString("1.23456789"), "1.23456789"}, - {"%F", pi, "3.14159"}, - {"%f", New(150, -2), "1.5"}, - {"%f", New(12, 3), "12000"}, - {"%.2f", pi, "3.14"}, - {"%.0f", pi, "3"}, - {"%.7f", pi, "3.1415900"}, - {"%.1f", RequireFromString("2.25"), "2.3"}, - {"%.1f", RequireFromString("-2.25"), "-2.3"}, - {"%.2f", RequireFromString("-0.001"), "0.00"}, - {"%2.2f", pi, "3.14"}, - {"%8.2f", pi, " 3.14"}, - {"%-8.2f|", pi, "3.14 |"}, - {"%08.2f", pi, "00003.14"}, - {"%08.2f", pi.Neg(), "-0003.14"}, - {"%-08.2f|", pi, "3.14 |"}, - {"%+.2f", pi, "+3.14"}, - {"%+.2f", pi.Neg(), "-3.14"}, - {"%+08.2f", pi, "+0003.14"}, - {"% .2f", pi, " 3.14"}, - - {"%#v", New(314, -2), "decimal.New(314, -2)"}, - {"%#v", New(-1500, -3), "decimal.New(-1500, -3)"}, - {"%#v", Decimal{}, "decimal.Decimal{}"}, - {"%#v", New(1, math.MaxInt32), "decimal.New(1, 2147483647)"}, - {"%#v", RequireFromString("-12345678901234567890123456789012").Shift(-2), `decimal.RequireFromString("-12345678901234567890123456789012").Shift(-2)`}, - {"%#v", RequireFromString("12345678901234567890").Shift(20000), `decimal.RequireFromString("12345678901234567890").Shift(20000)`}, - {"%#v", NewNullDecimal(New(314, -2)), "decimal.NullDecimal{Decimal:decimal.New(314, -2), Valid:true}"}, - - {"%d", pi, "%!d(decimal.Decimal=3.14159)"}, - {"%e", pi, "%!e(decimal.Decimal=3.14159)"}, - } { - if got := fmt.Sprintf(c.format, c.v); got != c.want { - t.Errorf("Sprintf(%q) of case %d: got %q, want %q", c.format, i, got, c.want) - } - } - - for _, c := range []struct { - got, want string - }{ - {fmt.Sprintln(pi, pi.Neg()), "3.14159 -3.14159\n"}, - {fmt.Sprintf("%8d|%f|%.2f|", 1, pi, pi), " 1|3.14159|3.14|"}, - } { - if c.got != c.want { - t.Errorf("got %q, want %q", c.got, c.want) - } - } -} - -func TestDecimal_FormatWithStringSettings(t *testing.T) { - defer func() { - TrimTrailingZeros = true - UseScientificNotation = false - }() - TrimTrailingZeros = false - UseScientificNotation = true - - for _, c := range []struct { - format string - d Decimal - want string - }{ - {"%v", New(150, -2), "1.50"}, - {"%f", New(150, -2), "1.50"}, - {"%v", New(1, 3), "1E3"}, - {"%f", New(1, 3), "1000"}, - } { - if got := fmt.Sprintf(c.format, c.d); got != c.want { - t.Errorf("Sprintf(%q, %se%d): got %q, want %q", c.format, c.d.Coefficient(), c.d.Exponent(), got, c.want) - } - } -} - -func TestDecimal_ScientificNotation(t *testing.T) { - type testData struct { - input string - expected string - } - - tests := []testData{ - {"1", "1E0"}, - {"1.0", "1.0E0"}, - {"10", "1.0E1"}, - {"123", "1.23E2"}, - {"1234", "1.234E3"}, - {"-1", "-1E0"}, - {"-10", "-1.0E1"}, - {"-123", "-1.23E2"}, - {"-1234", "-1.234E3"}, - {"0.1", "1E-1"}, - {"0.01", "1E-2"}, - {"0.123", "1.23E-1"}, - {"1.23", "1.23E0"}, - {"-0.1", "-1E-1"}, - {"-0.01", "-1E-2"}, - {"-0.010", "-1.0E-2"}, - {"-0.123", "-1.23E-1"}, - {"-1.23", "-1.23E0"}, - {"1E6", "1E6"}, - {"1e6", "1E6"}, - {"1.23E6", "1.23E6"}, - {"-1E6", "-1E6"}, - {"1E-6", "1E-6"}, - {"1.23E-6", "1.23E-6"}, - {"-1E-6", "-1E-6"}, - {"-1.0E-6", "-1.0E-6"}, - {"12345600", "1.2345600E7"}, - {"123456E2", "1.23456E7"}, - {"0", "0"}, - {"0E1", "0"}, - {"-0", "0"}, - {"-0.000", "0"}, - } - - for _, test := range tests { - d, err := NewFromString(test.input) - if err != nil { - t.Fatal(err) - } else if d.ScientificNotationString() != test.expected { - t.Errorf("expected %s, got %s", test.expected, d.ScientificNotationString()) - } - } -} - func ExampleNewFromFloat32() { fmt.Println(NewFromFloat32(123.123123123123).String()) fmt.Println(NewFromFloat32(.123123123123123).String()) fmt.Println(NewFromFloat32(-1e13).String()) // OUTPUT: - // 123.12312 - // 0.123123124 - // -10000000000000 + //123.12312 + //0.123123124 + //-10000000000000 } func ExampleNewFromFloat() { fmt.Println(NewFromFloat(123.123123123123).String()) fmt.Println(NewFromFloat(.123123123123123).String()) fmt.Println(NewFromFloat(-1e13).String()) - fmt.Println(NewFromFloat(1 << 62).String()) - fmt.Println(NewFromFloatWithExponent(1<<62, 0).String()) // OUTPUT: - // 123.123123123123 - // 0.123123123123123 - // -10000000000000 - // 4611686018427388000 - // 4611686018427387904 -} - -// TestTruncateNegativePrecision verifies that Truncate correctly handles -// negative precision values, truncating the integer part towards zero. -func TestTruncateNegativePrecision(t *testing.T) { - type testCase struct { - input string - places int32 - want string - wantExp int32 - } - tests := []testCase{ - // negative precision: truncate integer part - {"5432", -2, "5400", 2}, - {"-5432", -2, "-5400", 2}, - {"5499", -2, "5400", 2}, - {"5500", -2, "5500", 2}, - {"5501", -2, "5500", 2}, - {"999", -3, "0", 3}, - {"1000", -3, "1000", 3}, - {"1001", -3, "1000", 3}, - {"-999", -3, "0", 3}, - {"-1000", -3, "-1000", 3}, - // positive precision still works as before - {"123.456", 2, "123.45", -2}, - {"123.456", 0, "123", 0}, - {"123.456", 5, "123.456", -3}, - } - - for _, tc := range tests { - d := RequireFromString(tc.input) - got := d.Truncate(tc.places) - if got.String() != tc.want { - t.Errorf("(%s).Truncate(%d): got %s, want %s", tc.input, tc.places, got.String(), tc.want) - } - if got.exp != tc.wantExp { - t.Errorf("(%s).Truncate(%d): got exponent %d, want %d", tc.input, tc.places, got.exp, tc.wantExp) - } - } -} - -func TestRescaleToZeroRepresentation(t *testing.T) { - // reflect.DeepEqual, used by testify's assert.Equal, also compares the big.Int internals. - // Rounding a nonzero value to zero keeps the same form for any number of dropped digits. - want := New(7, 0).Sub(New(7, 0)) - for _, k := range []int32{1, 19, 20, 40} { - d := New(5, -k) - if got := d.RoundDown(0); !reflect.DeepEqual(got, want) { - t.Errorf("5e-%d RoundDown(0) = %#v, want the same representation as %#v", k, got, want) - } - if got := d.Truncate(0); !reflect.DeepEqual(got, want) { - t.Errorf("5e-%d Truncate(0) = %#v, want the same representation as %#v", k, got, want) - } - } -} - -// TestRoundingExponentNormalization verifies that RoundUp, RoundDown, -// RoundCeil, and RoundFloor return a result with the exponent normalized -// to the requested number of places, even when the value is already exact. -func TestRoundCeilFloorToZeroRepresentation(t *testing.T) { - // reflect.DeepEqual, used by testify's assert.Equal, also compares the big.Int internals. - // Rounding a nonzero value to zero keeps the same form for any number of dropped digits. - want := New(7, 0).Sub(New(7, 0)) - for _, k := range []int32{1, 19, 20, 40} { - if got := New(-5, -k).RoundCeil(0); !reflect.DeepEqual(got, want) { - t.Errorf("-5e-%d RoundCeil(0) = %#v, want the same representation as %#v", k, got, want) - } - if got := New(5, -k).RoundFloor(0); !reflect.DeepEqual(got, want) { - t.Errorf("5e-%d RoundFloor(0) = %#v, want the same representation as %#v", k, got, want) - } - } -} - -func TestRoundingExponentNormalization(t *testing.T) { - type testCase struct { - fn string - input string - places int32 - wantStr string - wantExp int32 - } - - tests := []testCase{ - // RoundUp - {"RoundUp", "100.0", 0, "100", 0}, - {"RoundUp", "100.00", 0, "100", 0}, - {"RoundUp", "3.14", 2, "3.14", -2}, - {"RoundUp", "500", -2, "500", 2}, - {"RoundUp", "100", 2, "100", -2}, - {"RoundUp", "5E3", -2, "5000", 2}, - {"RoundUp", "0.01230000000000000000000000000000", 4, "0.0123", -4}, - // RoundDown - {"RoundDown", "100.0", 0, "100", 0}, - {"RoundDown", "100.00", 0, "100", 0}, - {"RoundDown", "3.14", 2, "3.14", -2}, - {"RoundDown", "500", -2, "500", 2}, - {"RoundDown", "100", 2, "100", -2}, - {"RoundDown", "5E3", -2, "5000", 2}, - // RoundFloor - {"RoundFloor", "100.0", 0, "100", 0}, - {"RoundFloor", "100.00", 0, "100", 0}, - {"RoundFloor", "-100.0", 0, "-100", 0}, - {"RoundFloor", "500", -2, "500", 2}, - {"RoundFloor", "-100", 2, "-100", -2}, - {"RoundFloor", "5E3", -2, "5000", 2}, - // RoundCeil - {"RoundCeil", "100.0", 0, "100", 0}, - {"RoundCeil", "100.00", 0, "100", 0}, - {"RoundCeil", "-100.0", 0, "-100", 0}, - {"RoundCeil", "500", -2, "500", 2}, - {"RoundCeil", "-100", 2, "-100", -2}, - {"RoundCeil", "5E3", -2, "5000", 2}, - } - - for _, tc := range tests { - d := RequireFromString(tc.input) - var got Decimal - switch tc.fn { - case "RoundUp": - got = d.RoundUp(tc.places) - case "RoundDown": - got = d.RoundDown(tc.places) - case "RoundFloor": - got = d.RoundFloor(tc.places) - case "RoundCeil": - got = d.RoundCeil(tc.places) - } - if got.String() != tc.wantStr { - t.Errorf("(%s).%s(%d): got %s, want %s", tc.input, tc.fn, tc.places, got.String(), tc.wantStr) - } - if got.exp != tc.wantExp { - t.Errorf("(%s).%s(%d): got exponent %d, want %d", tc.input, tc.fn, tc.places, got.exp, tc.wantExp) - } - } + //123.123123123123 + //0.123123123123123 + //-10000000000000 } diff --git a/encoding.go b/encoding.go deleted file mode 100644 index 6801f99..0000000 --- a/encoding.go +++ /dev/null @@ -1,465 +0,0 @@ -package decimal - -import ( - "database/sql/driver" - "encoding/binary" - "fmt" - "math" - "math/big" - "reflect" -) - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error { - if string(decimalBytes) == "null" { - return nil - } - - decimal, err := NewFromString(unquoteIfQuoted(string(decimalBytes))) - *d = decimal - if err != nil { - return fmt.Errorf("error decoding string '%s': %s", string(decimalBytes), err) - } - return nil -} - -// MarshalJSON implements the json.Marshaler interface. -func (d Decimal) MarshalJSON() ([]byte, error) { - str := d.String() - if MarshalJSONWithoutQuotes { - return []byte(str), nil - } - b := make([]byte, 0, len(str)+2) - b = append(b, '"') - b = append(b, str...) - return append(b, '"'), nil -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation -// is already used when encoding to text, this method stores that string as []byte -func (d *Decimal) UnmarshalBinary(data []byte) error { - // Verify we have at least 4 bytes for the exponent. The GOB encoded value - // may be empty. - if len(data) < 4 { - return fmt.Errorf("error decoding binary %v: expected at least 4 bytes, got %d", data, len(data)) - } - - // Extract the exponent - exp := int32(binary.BigEndian.Uint32(data[:4])) - if int64(exp) > int64(MaxDecodeExponent) || int64(exp) < -int64(MaxDecodeExponent) { - return fmt.Errorf("error decoding binary: exponent %d exceeds MaxDecodeExponent (%d)", exp, MaxDecodeExponent) - } - d.exp = exp - - // Extract the value - d.value = new(big.Int) - if err := d.value.GobDecode(data[4:]); err != nil { - return fmt.Errorf("error decoding binary %v: %s", data, err) - } - - return nil -} - -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (d Decimal) MarshalBinary() (data []byte, err error) { - // exp is written first, but encode value first to know output size - var valueData []byte - if valueData, err = d.getValue().GobEncode(); err != nil { - return nil, err - } - - // Write the exponent in front, since it's a fixed size - expData := make([]byte, 4, len(valueData)+4) - binary.BigEndian.PutUint32(expData, uint32(d.exp)) - - // Return the byte array - return append(expData, valueData...), nil -} - -// Scan implements the sql.Scanner interface for database deserialization. -func (d *Decimal) Scan(value interface{}) error { - // first try to see if the data is stored in database as a Numeric datatype - switch v := value.(type) { - - case nil: - return fmt.Errorf("can't scan NULL into a Decimal, use NullDecimal or *Decimal") - - case float32: - if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) { - return fmt.Errorf("could not convert value '%v' to decimal", v) - } - *d = NewFromFloat32(v) - return nil - - case float64: - // numeric in sqlite3 sends us float64 - if math.IsNaN(v) || math.IsInf(v, 0) { - return fmt.Errorf("could not convert value '%v' to decimal", v) - } - *d = NewFromFloat(v) - return nil - - case int64: - // at least in sqlite3 when the value is 0 in db, the data is sent - // to us as an int64 instead of a float64 ... - *d = New(v, 0) - return nil - - case uint64: - // while clickhouse may send 0 in db as uint64 - *d = NewFromUint64(v) - return nil - - case int, int8, int16, int32: - // clickhouse and test mocks send the other integer sizes - *d = New(reflect.ValueOf(v).Int(), 0) - return nil - - case uint, uint8, uint16, uint32: - *d = NewFromUint64(reflect.ValueOf(v).Uint()) - return nil - - case big.Int: - // clickhouse sends Int128, UInt128 and Int256 through database/sql as big.Int - *d = NewFromBigInt(&v, 0) - return nil - - case *big.Int: - if v != nil { - *d = NewFromBigInt(v, 0) - return nil - } - - case Decimal: - // clickhouse sends Decimal columns through its native API as Decimal - *d = v - return nil - - case string: - var err error - *d, err = NewFromString(unquoteIfQuoted(v)) - return err - - case []byte: - var err error - *d, err = NewFromString(unquoteIfQuoted(string(v))) - return err - } - return fmt.Errorf("could not convert value '%+v' to any known type", value) -} - -// Value implements the driver.Valuer interface for database serialization. -func (d Decimal) Value() (driver.Value, error) { - return d.String(), nil -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface for XML -// deserialization. -func (d *Decimal) UnmarshalText(text []byte) error { - str := string(text) - - dec, err := NewFromString(str) - *d = dec - if err != nil { - return fmt.Errorf("error decoding string '%s': %s", str, err) - } - - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface for XML -// serialization. -func (d Decimal) MarshalText() (text []byte, err error) { - return []byte(d.String()), nil -} - -// GobEncode implements the gob.GobEncoder interface for gob serialization. -func (d Decimal) GobEncode() ([]byte, error) { - return d.MarshalBinary() -} - -// GobDecode implements the gob.GobDecoder interface for gob serialization. -func (d *Decimal) GobDecode(data []byte) error { - return d.UnmarshalBinary(data) -} - -// DecodeSpanner decodes a Spanner value into a Decimal -func (d *Decimal) DecodeSpanner(val interface{}) error { - return d.Scan(val) -} - -// EncodeSpanner encodes a Decimal into a Spanner value -func (d Decimal) EncodeSpanner() (interface{}, error) { - return d.String(), nil -} - -// BSON type numbers, see https://bsonspec.org/spec.html -const ( - bsonTypeDouble byte = 0x01 - bsonTypeString byte = 0x02 - bsonTypeUndefined byte = 0x06 - bsonTypeNull byte = 0x0A - bsonTypeInt32 byte = 0x10 - bsonTypeInt64 byte = 0x12 - bsonTypeDecimal128 byte = 0x13 -) - -// MarshalBSONValue implements the bson.ValueMarshaler interface of the MongoDB Go driver v2. It stores -// the decimal as a BSON Decimal128, MongoDB's decimal type, which holds 34 significant digits and -// exponents from -6176 to 6111. Trailing zeros move between the coefficient and the exponent to fit, so -// Exponent() can change in a round trip but the value can't. Decimals that still don't fit return an -// error instead of being rounded. -// -// The v1 driver declares this interface with its own bsontype.Type, so it doesn't call this method. -func (d Decimal) MarshalBSONValue() (byte, []byte, error) { - coef := new(big.Int).Abs(d.getValue()) - exp := d.exp - - // Zero fits at any exponent, and the loops below could take 2^31 steps to get there. - if coef.Sign() == 0 { - if exp < decimal128MinExp { - exp = decimal128MinExp - } else if exp > decimal128MaxExp { - exp = decimal128MaxExp - } - } - - quo, rem := new(big.Int), new(big.Int) - for (coef.Cmp(decimal128MaxCoef) > 0 || exp < decimal128MinExp) && exp < decimal128MaxExp { - if quo.QuoRem(coef, tenInt, rem); rem.Sign() != 0 { - break - } - coef, quo = quo, coef - exp++ - } - for exp > decimal128MaxExp && coef.Cmp(decimal128MaxCoef) <= 0 { - coef.Mul(coef, tenInt) - exp-- - } - if coef.Cmp(decimal128MaxCoef) > 0 || exp < decimal128MinExp { - return 0, nil, fmt.Errorf("can't encode %se%d as BSON Decimal128 without rounding: it holds 34 significant digits and exponents from %d to %d", - d.getValue(), d.exp, decimal128MinExp, decimal128MaxExp) - } - return bsonTypeDecimal128, encodeDecimal128(d.Sign() < 0, coef, exp), nil -} - -// UnmarshalBSONValue implements the bson.ValueUnmarshaler interface of the MongoDB Go driver v2. -// It decodes BSON Decimal128, string, double, int32 and int64 values. BSON null and undefined decode -// to zero, as they do for the driver's own number types. -func (d *Decimal) UnmarshalBSONValue(typ byte, data []byte) error { - var err error - switch { - case typ == bsonTypeNull || typ == bsonTypeUndefined: - *d = Decimal{} - case typ == bsonTypeDecimal128 && len(data) == 16: - *d, err = decodeDecimal128(data) - case typ == bsonTypeString && len(data) >= 5: - // int32 length, the string, then a NUL byte - *d, err = NewFromString(string(data[4 : len(data)-1])) - case typ == bsonTypeDouble && len(data) == 8: - f := math.Float64frombits(binary.LittleEndian.Uint64(data)) - if math.IsNaN(f) || math.IsInf(f, 0) { - return fmt.Errorf("can't convert BSON double %v to decimal", f) - } - *d = NewFromFloat(f) - case typ == bsonTypeInt32 && len(data) == 4: - *d = New(int64(int32(binary.LittleEndian.Uint32(data))), 0) - case typ == bsonTypeInt64 && len(data) == 8: - *d = New(int64(binary.LittleEndian.Uint64(data)), 0) - default: - return fmt.Errorf("can't convert BSON value of type 0x%02x and length %d to decimal", typ, len(data)) - } - return err -} - -// BSON stores a Decimal128 as a little-endian IEEE 754-2008 decimal128 in the binary integer decimal -// encoding: from the top bit down, a sign bit, a 14-bit exponent biased by 6176 and a 113-bit coefficient. -const ( - decimal128MinExp = -6176 - decimal128MaxExp = 6111 -) - -// decimal128MaxCoef is 10^34 - 1, the largest Decimal128 coefficient. -var decimal128MaxCoef = new(big.Int).Sub(new(big.Int).Exp(tenInt, big.NewInt(34), nil), oneInt) - -// encodeDecimal128 expects a coefficient and exponent that are already in range. -func encodeDecimal128(neg bool, coef *big.Int, exp int32) []byte { - var buf [16]byte - b := coef.Bytes() - copy(buf[16-len(b):], b) - hi := binary.BigEndian.Uint64(buf[:8]) | uint64(exp-decimal128MinExp)<<49 - if neg { - hi |= 1 << 63 - } - data := make([]byte, 16) - binary.LittleEndian.PutUint64(data, binary.BigEndian.Uint64(buf[8:])) - binary.LittleEndian.PutUint64(data[8:], hi) - return data -} - -func decodeDecimal128(data []byte) (Decimal, error) { - lo := binary.LittleEndian.Uint64(data) - hi := binary.LittleEndian.Uint64(data[8:]) - neg := hi>>63 == 1 - - var exp int32 - switch { - case hi>>59&0xF == 0xF: - return Decimal{}, fmt.Errorf("can't convert BSON Decimal128 NaN or Infinity to decimal") - case hi>>61&3 == 3: - // The exponent starts 2 bits lower, and the coefficient is always above 10^34 - 1. - exp, hi, lo = int32(hi>>47&0x3FFF), 0, 0 - default: - exp, hi = int32(hi>>49&0x3FFF), hi&(1<<49-1) - } - exp += decimal128MinExp - if int64(exp) > int64(MaxDecodeExponent) || int64(exp) < -int64(MaxDecodeExponent) { - return Decimal{}, fmt.Errorf("can't convert BSON Decimal128 to decimal: exponent %d exceeds MaxDecodeExponent (%d)", exp, MaxDecodeExponent) - } - - var buf [16]byte - binary.BigEndian.PutUint64(buf[:8], hi) - binary.BigEndian.PutUint64(buf[8:], lo) - value := new(big.Int).SetBytes(buf[:]) - // IEEE 754 reads coefficients above 10^34 - 1 as zero. - if value.Cmp(decimal128MaxCoef) > 0 { - value.SetInt64(0) - } - if neg { - value.Neg(value) - } - return Decimal{value: value, exp: exp}, nil -} - -func unquoteIfQuoted(value string) string { - // If the amount is quoted, strip the quotes - if len(value) > 2 && value[0] == '"' && value[len(value)-1] == '"' { - return value[1 : len(value)-1] - } - - return value -} - -// NullDecimal represents a nullable decimal with compatibility for -// scanning null values from the database. -type NullDecimal struct { - Decimal Decimal - Valid bool -} - -// NewNullDecimal returns a valid NullDecimal holding d. -func NewNullDecimal(d Decimal) NullDecimal { - return NullDecimal{ - Decimal: d, - Valid: true, - } -} - -// Scan implements the sql.Scanner interface for database deserialization. -func (d *NullDecimal) Scan(value interface{}) error { - if value == nil { - d.Valid = false - return nil - } - err := d.Decimal.Scan(value) - d.Valid = err == nil - return err -} - -// Value implements the driver.Valuer interface for database serialization. -func (d NullDecimal) Value() (driver.Value, error) { - if !d.Valid { - return nil, nil - } - return d.Decimal.Value() -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error { - if string(decimalBytes) == "null" { - d.Valid = false - return nil - } - err := d.Decimal.UnmarshalJSON(decimalBytes) - d.Valid = err == nil - return err -} - -// MarshalJSON implements the json.Marshaler interface. -func (d NullDecimal) MarshalJSON() ([]byte, error) { - if !d.Valid { - return []byte("null"), nil - } - return d.Decimal.MarshalJSON() -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface for XML -// deserialization -func (d *NullDecimal) UnmarshalText(text []byte) error { - str := string(text) - - // check for empty XML or XML without body e.g., - if str == "" { - d.Valid = false - return nil - } - if err := d.Decimal.UnmarshalText(text); err != nil { - d.Valid = false - return err - } - d.Valid = true - return nil -} - -// MarshalText implements the encoding.TextMarshaler interface for XML -// serialization. -func (d NullDecimal) MarshalText() (text []byte, err error) { - if !d.Valid { - return []byte{}, nil - } - return d.Decimal.MarshalText() -} - -// DecodeSpanner decodes a Spanner value into a Decimal -func (d *NullDecimal) DecodeSpanner(value interface{}) error { - switch t := value.(type) { - case nil: - d.Valid = false - return nil - case *string: - if t == nil { - d.Valid = false - return nil - } - value = *t - } - - err := d.Decimal.Scan(value) - d.Valid = err == nil - return err -} - -// EncodeSpanner encodes a Decimal into a Spanner value -func (d NullDecimal) EncodeSpanner() (interface{}, error) { - if !d.Valid { - return nil, nil - } - return d.Decimal.String(), nil -} - -// MarshalBSONValue implements the bson.ValueMarshaler interface of the MongoDB Go driver v2. -// An invalid NullDecimal is stored as BSON null, see Decimal.MarshalBSONValue for the rest. -func (d NullDecimal) MarshalBSONValue() (byte, []byte, error) { - if !d.Valid { - return bsonTypeNull, nil, nil - } - return d.Decimal.MarshalBSONValue() -} - -// UnmarshalBSONValue implements the bson.ValueUnmarshaler interface of the MongoDB Go driver v2. -// BSON null and undefined make the NullDecimal invalid, see Decimal.UnmarshalBSONValue for the rest. -func (d *NullDecimal) UnmarshalBSONValue(typ byte, data []byte) error { - err := d.Decimal.UnmarshalBSONValue(typ, data) - d.Valid = err == nil && typ != bsonTypeNull && typ != bsonTypeUndefined - return err -} diff --git a/go.mod b/go.mod index 3d26474..ae1b7aa 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module github.com/shopspring/decimal -go 1.10 +go 1.13 diff --git a/math.go b/math.go deleted file mode 100644 index e5c7d5c..0000000 --- a/math.go +++ /dev/null @@ -1,1143 +0,0 @@ -package decimal - -import ( - "fmt" - "math" - "math/big" - "math/bits" - "sync" -) - -var factorials = []Decimal{New(1, 0)} -var factorialsMutex sync.RWMutex - -// Pow returns d to the power of d2. -// The result is exact for non-negative integer exponents. For negative or non-integer exponents it is -// rounded half away from zero to PowPrecisionNegativeExponent places after the decimal point. A result -// that would round to 0 keeps PowPrecisionNegativeExponent significant digits instead (at least one). -// -// 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 -// - x ** y, where the magnitude of the result or the precision is too large to represent -// -// 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.0803715950193171" -// -// d5 := decimal.NewFromInt(10) -// d6 := decimal.NewFromInt(-18) -// res3 := d5.Pow(d6) -// res3.String() // output: "0.000000000000000001" -func (d Decimal) Pow(d2 Decimal) Decimal { - res, err := d.PowWithPrecision(d2, int32(PowPrecisionNegativeExponent)) - if err != nil { - return Decimal{} - } - return res -} - -// PowWithPrecision returns d to the power of d2. -// The result is exact for non-negative integer exponents. For negative or non-integer exponents it is -// rounded half away from zero to precision places after the decimal point. A result that would round -// to 0 keeps precision significant digits instead (at least one). -// -// 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 -// - x ** y, where the magnitude of the result or the precision is too large to represent -// -// 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.08037" -// -// 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) { - if d.IsZero() { - if d2.IsZero() { - return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0") - } - if d2.Sign() < 0 { - return Decimal{}, fmt.Errorf("cannot represent infinity value of 0 ** y, where y < 0") - } - return Decimal{zeroInt, 0}, nil - } - - if d2.IsInteger() { - return d.powBigIntWithPrecision(d2.BigInt(), precision) - } - - if d.Sign() < 0 { - return Decimal{}, fmt.Errorf("cannot represent imaginary value of x ** y, where x < 0 and y is non-integer decimal") - } - - return d.powFrac(d2, precision) -} - -// PowInt32 returns d to the power of exp, where exp is int32. -// Returns error for 0 ** 0, 0 ** exp where exp < 0, and results whose magnitude is too large to represent. -// -// For negative exponents the result is rounded half away from zero to PowPrecisionNegativeExponent places -// after the decimal point. A result that would round to 0 keeps PowPrecisionNegativeExponent significant -// digits instead (at least one). -// -// 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) { - return d.PowBigInt(big.NewInt(int64(exp))) -} - -// PowBigInt returns d to the power of exp, where exp is big.Int. -// Returns error for 0 ** 0, 0 ** exp where exp < 0, and results whose magnitude is too large to represent. -// -// For negative exponents the result is rounded half away from zero to PowPrecisionNegativeExponent places -// after the decimal point. A result that would round to 0 keeps PowPrecisionNegativeExponent significant -// digits instead (at least one). -// -// 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)) -} - -var errPowOutOfRange = fmt.Errorf("cannot represent result of power operation, its magnitude or precision is too large") - -// powExactBits is the largest estimated cost of computing d^n exactly for negative n. -// Bigger powers are approximated with truncated intermediates, which is cheaper. -const powExactBits = 3000 - -func (d Decimal) powBigIntWithPrecision(exp *big.Int, precision int32) (Decimal, error) { - switch { - case exp.Sign() > 0: - return d.powExact(exp) - case d.IsZero() && exp.Sign() == 0: - return Decimal{}, fmt.Errorf("cannot represent undefined value of 0**0") - case exp.Sign() == 0: - return Decimal{oneInt, 0}, nil - case d.IsZero(): - return Decimal{}, fmt.Errorf("cannot represent infinity value of 0 ** y, where y < 0") - } - return d.powNegInt(new(big.Int).Neg(exp), precision) -} - -// powExact returns d^n for n > 0. -func (d Decimal) powExact(n *big.Int) (Decimal, error) { - exp := new(big.Int).Mul(n, big.NewInt(int64(d.exp))) - if !exp.IsInt64() || exp.Int64() > math.MaxInt32 || exp.Int64() < math.MinInt32 { - return Decimal{}, errPowOutOfRange - } - return Decimal{new(big.Int).Exp(d.getValue(), n, nil), int32(exp.Int64())}, nil -} - -// powNegInt returns d^-n for n > 0, rounded as described in PowWithPrecision. -func (d Decimal) powNegInt(n *big.Int, precision int32) (Decimal, error) { - if d.absIsOne() { - // only the parity of n matters - n = big.NewInt(2 - int64(n.Bit(0))) - } - - // small powers are cheap to compute exactly, and |log10(d^-n)| <= cost keeps them in range - if n.IsInt64() && n.Int64() <= powExactBits { - cost := n.Int64() * (int64(d.getValue().BitLen()) + abs64(int64(d.exp))) - if cost <= powExactBits && powInRange(float64(cost), precision) { - return d.powNegIntExact(n, precision) - } - } - - nf, _ := new(big.Float).SetInt(n).Float64() - lg := -nf * d.log10Abs() - if !powInRange(lg, precision) { - return Decimal{}, errPowOutOfRange - } - - // the digit shifts of the truncated c^n must fit into int64 - if nd := int64(d.NumDigits()); n.IsInt64() && n.Int64() <= math.MaxInt64/(4*nd) { - c := new(big.Int).Abs(d.getValue()) - w := powSigDigits(lg, precision) + int64(len(n.String())) + 6 - // each try adds 20 digits, failing 5 times means an exact tie or a value extremely close to one - for i := 0; i < 5; i, w = i+1, w+20 { - if res, ok := powNegIntApprox(c, int64(d.exp), n, w, precision); ok { - if d.Sign() < 0 && n.Bit(0) == 1 { - res.value.Neg(res.value) - } - return res, nil - } - } - } - - // same as for non-integer exponents, which handles any n and exact ties - res, err := d.Abs().powFrac(Decimal{value: new(big.Int).Neg(n)}, precision) - if err == nil && d.Sign() < 0 && n.Bit(0) == 1 { - res.value.Neg(res.value) - } - return res, err -} - -// powNegIntExact returns d^-n for n > 0, dividing by the exact d^n. -func (d Decimal) powNegIntExact(n *big.Int, precision int32) (Decimal, error) { - x, err := d.powExact(n) - if err != nil { - return Decimal{}, err - } - res := New(1, 0).DivRound(x, precision) - if res.IsZero() { - // first significant digit of 1/x is at place NumDigits+exp, one earlier when x is a power of ten - nd := int32(x.NumDigits()) - places := int32(powMinSig(precision)) - 1 + nd + x.exp - if x.getValue().CmpAbs(pow10(int64(nd)-1)) == 0 { - places-- - } - res = New(1, 0).DivRound(x, places) - } - return res, nil -} - -// powNegIntApprox rounds |1 / (c^n * 10^(e*n))| as described in PowWithPrecision. c^n is approximated -// from below by m*10^s, truncating m to w digits after every multiplication. Each truncation shrinks -// the result by a factor of at most 1-10^(1-w), and the errors add up to less than 5n*10^(1-w), -// lo below allows ten times that. -func powNegIntApprox(c *big.Int, e int64, n *big.Int, w int64, precision int32) (Decimal, bool) { - cw := new(big.Int).Set(c) - cs, exact := truncDigits(cw, w) - - m, s := big.NewInt(1), int64(0) - for i := n.BitLen() - 1; i >= 0; i-- { - m.Mul(m, m) - ds, ex := truncDigits(m, w) - s, exact = 2*s+ds, exact && ex - if n.Bit(i) == 1 { - m.Mul(m, cw) - ds, ex = truncDigits(m, w) - s, exact = s+cs+ds, exact && ex - } - } - - // 10^scale / (m * 10^(s+e*n)) = 10^z / m - z := int64(Decimal{value: m}.NumDigits()) + w + 2 - scale := z + s + e*n.Int64() - q, rem := new(big.Int).QuoRem(pow10(z), m, new(big.Int)) - lo, hi := q, q - if !exact || rem.Sign() != 0 { - hi = new(big.Int).Add(q, oneInt) - } - if !exact { - lo = new(big.Int).Mul(q, big.NewInt(5)) - lo.Mul(lo, n).Quo(lo, pow10(w-2)) - lo.Sub(q, lo).Sub(lo, oneInt) - } - return roundBounds(lo, hi, scale, precision) -} - -// truncDigits truncates m > 0 to at least w digits, returning the number of dropped digits -// and whether they were all zeros. -func truncDigits(m *big.Int, w int64) (int64, bool) { - // m >= 2^(BitLen-1), so m has at least floor((BitLen-1)*log10(2))+1 digits - drop := int64(float64(m.BitLen()-1)*math.Log10(2)-1e-9) + 1 - w - if drop <= 0 { - return 0, true - } - var r big.Int - m.QuoRem(m, pow10(drop), &r) - return drop, r.Sign() == 0 -} - -// powFrac returns d^y for d > 0 and non-integer (or negative integer) y, rounded as described in -// PowWithPrecision. It evaluates d^y = 10^q * e^s, where y*ln(d) = q*ln(10) + s and 0 <= s < ln(10), -// in binary fixed-point arithmetic, adding working digits until the error bounds decide the rounding. -func (d Decimal) powFrac(y Decimal, precision int32) (Decimal, error) { - if d.absIsOne() { - // 1^y = 1, also for y beyond float64 range - if !powInRange(0, precision) { - return Decimal{}, errPowOutOfRange - } - k := int64(abs(precision)) + 2 - res, _ := roundBounds(pow10(k), pow10(k), k, precision) - return res, nil - } - - yf := y.InexactFloat64() - lg := yf * d.log10Abs() - if !powInRange(lg, precision) { - return Decimal{}, errPowOutOfRange - } - - k := int64(d.NumDigits()) - 1 + int64(d.exp) - // covers the working error amplified by |y| and |k|, see powFracFixed - extra := int64(math.Log10(math.Abs(yf)*(math.Abs(float64(k))+64)+math.Abs(lg)+1)) + 4 - // 9 guard digits make a retry unlikely, each retry adds 20 more - f := powSigDigits(lg, precision) + 9 - exactTie := false - for i := 0; ; i, f = i+1, f+20 { - if res, ok := powFracFixed(d, y, k, f, extra, precision, exactTie); ok { - return res, nil - } - // only a terminating result can sit exactly on a rounding boundary, which no working - // precision resolves, then the upper bound rounds it away from zero - if i == 8 { - exactTie = powIsTerminating(d, y) - } - } -} - -// powIsTerminating reports whether d^y is a terminating decimal, for d > 0. -func powIsTerminating(d, y Decimal) bool { - p, q := powRatio(y) - num, den := powRatio(d) - if !q.IsInt64() { - return false - } - // d^(p/q) is rational only when num and den are perfect q-th powers - rn, ok := iroot(num, q.Int64()) - if !ok { - return false - } - rd, ok := iroot(den, q.Int64()) - if !ok { - return false - } - // (rn/rd)^p terminates when its denominator has no prime factors other than 2 and 5 - if p.Sign() < 0 { - rd = rn - } - rd = new(big.Int).Set(rd) - for _, f := range []*big.Int{twoInt, fiveInt} { - for r := new(big.Int); rd.Cmp(oneInt) > 0 && r.Rem(rd, f).Sign() == 0; { - rd.Quo(rd, f) - } - } - return rd.Cmp(oneInt) == 0 -} - -// powRatio returns d as a fraction p/q in lowest terms, q > 0. -func powRatio(d Decimal) (*big.Int, *big.Int) { - if d.exp >= 0 { - return new(big.Int).Mul(d.getValue(), pow10(int64(d.exp))), big.NewInt(1) - } - p, q := new(big.Int).Set(d.getValue()), new(big.Int).Set(pow10(-int64(d.exp))) - g := new(big.Int).GCD(nil, nil, new(big.Int).Abs(p), q) - return p.Quo(p, g), q.Quo(q, g) -} - -// iroot returns the integer k-th root of x >= 1, rounded down, and whether it is exact. -func iroot(x *big.Int, k int64) (*big.Int, bool) { - if k == 1 || x.Cmp(oneInt) == 0 { - return x, true - } - if int64(x.BitLen()) <= k { - // 1 < x < 2^k - return big.NewInt(1), false - } - // Newton's method from above converges to the floor of the root - kb, km1 := big.NewInt(k), big.NewInt(k-1) - r := new(big.Int).Lsh(oneInt, uint((int64(x.BitLen())+k-1)/k)) - t, u := new(big.Int), new(big.Int) - for { - t.Quo(x, t.Exp(r, km1, nil)) - t.Add(t, u.Mul(r, km1)).Quo(t, kb) - if t.Cmp(r) >= 0 { - break - } - r.Set(t) - } - return r, t.Exp(r, kb, nil).Cmp(x) == 0 -} - -// powFracFixed computes d^y (see powFrac) as an interval of e^s * 10^f with f+extra working digits -// and rounds it. With force the upper bound is used as the exact value. -func powFracFixed(d, y Decimal, k, f, extra int64, precision int32, force bool) (Decimal, bool) { - b := uint(float64(f+extra)*math.Log2(10)) + 1 - - // d = m * 10^k, 1 <= m < 10 - m := new(big.Int).Lsh(d.getValue(), b) - m.Quo(m, pow10(int64(d.NumDigits())-1)) - t, lnErr := lnFixed(m, b) - ln10, ln10Err := ln10Fixed(b) - t.Add(t, new(big.Int).Mul(big.NewInt(k), ln10)) - tErr := big.NewInt(lnErr + 1 + abs64(k)*ln10Err) - - // t = y * ln(d) - t.Mul(t, y.getValue()) - if y.exp >= 0 { - t.Mul(t, pow10(int64(y.exp))) - } else { - t.Quo(t, pow10(-int64(y.exp))) - } - tErr.Mul(tErr, y.Abs().Ceil().getValue()).Add(tErr, oneInt) - - q, s := new(big.Int).DivMod(t, ln10, new(big.Int)) - sErr := new(big.Int).Abs(q) - sErr.Mul(sErr, big.NewInt(ln10Err)).Add(sErr, tErr) - - // e^s < 11, so an error in s grows at most 11 times - r, rErr := expFixed(s, b) - sErr.Mul(sErr, big.NewInt(11)).Add(sErr, big.NewInt(rErr+1)) - - // v * 10^(f-q) = e^s * 10^f - p := pow10(f) - hi := new(big.Int).Add(r, sErr) - hi.Mul(hi, p).Rsh(hi, b).Add(hi, oneInt) - lo := hi - if !force { - lo = new(big.Int).Sub(r, sErr) - lo.Mul(lo, p).Rsh(lo, b) - if lo.Sign() < 0 { - lo.SetInt64(0) - } - } - return roundBounds(lo, hi, f-q.Int64(), precision) -} - -// lnFixed returns ln(m / 2^b) * 2^b for m / 2^b in [1, 10], with its error bound in ulps. -func lnFixed(m *big.Int, b uint) (*big.Int, int64) { - // a = math.Log(m) is accurate to ~1e-15, so ln(m) = a + ln(1+z), where z = m*e^-a - 1 is tiny - // and the series ln(1+z) = z - z^2/2 + z^3/3 - ... gains ~50 bits per term. - top, sh := new(big.Int), m.BitLen()-60 - if sh > 0 { - top.Rsh(m, uint(sh)) - } else { - top.Lsh(m, uint(-sh)) - } - a := big.NewInt(int64(math.Ldexp(math.Log(math.Ldexp(float64(top.Uint64()), sh-int(b))), 52))) - if b >= 52 { - a.Lsh(a, b-52) - } else { - a.Rsh(a, 52-b) - } - - e, eErr := expFixed(new(big.Int).Neg(a), b) - z := e.Mul(e, m).Rsh(e, b) - z.Sub(z, new(big.Int).Lsh(oneInt, b)) - - sum, p, term, iv := new(big.Int).Set(z), new(big.Int).Set(z), new(big.Int), new(big.Int) - terms := int64(1) - for i := int64(2); ; i++ { - p.Mul(p, z).Rsh(p, b) - if p.Sign() == 0 { - break - } - term.Quo(p, iv.SetInt64(i)) - if i%2 == 0 { - sum.Sub(sum, term) - } else { - sum.Add(sum, term) - } - terms++ - } - // m <= 10 amplifies the error of e^-a at most 10 times - return sum.Add(sum, a), 10*eErr + 2*terms + 3 -} - -// expFixed returns e^(x / 2^b) * 2^b for |x / 2^b| <= 2.4, with its error bound in ulps. -func expFixed(x *big.Int, b uint) (*big.Int, int64) { - // e^x = (e^(x/2^j))^(2^j): Taylor series of the reduced argument, then j squarings. - // Guard bits cover the error of up to ~g Taylor terms, amplified up to 11*2^j times by squaring. - j := uint(math.Sqrt(float64(b))) + 1 - g := b + j + uint(bits.Len(33*(b+j+64)+363)) + 2 - - xr := new(big.Int).Lsh(x, g-b) - xr.Rsh(xr, j) - sum, term, iv := new(big.Int).Lsh(oneInt, g), new(big.Int).Lsh(oneInt, g), new(big.Int) - for i := int64(1); ; i++ { - term.Mul(term, xr).Rsh(term, g) - term.Quo(term, iv.SetInt64(i)) - if term.Sign() == 0 { - break - } - sum.Add(sum, term) - } - for ; j > 0; j-- { - sum.Mul(sum, sum).Rsh(sum, g) - } - return sum.Rsh(sum, g-b), 2 -} - -var ln10Cache struct { - once sync.Once - v *big.Int -} - -const ln10CacheBits = 2048 - -// ln10Fixed returns ln(10) * 2^b, with its error bound in ulps. -func ln10Fixed(b uint) (*big.Int, int64) { - if b <= ln10CacheBits { - ln10Cache.once.Do(func() { ln10Cache.v, _ = ln10FromDigits(ln10CacheBits) }) - return new(big.Int).Rsh(ln10Cache.v, ln10CacheBits-b), 3 - } - if v, ok := ln10FromDigits(b); ok { - return v, 2 - } - return lnFixed(new(big.Int).Lsh(big.NewInt(10), b), b) -} - -// ln10FromDigits returns ln(10) * 2^b computed from strLn10, if it has enough digits. -func ln10FromDigits(b uint) (*big.Int, bool) { - digits := int(float64(b)*math.Log10(2)) + 3 - if digits+2 > len(strLn10) { - return nil, false - } - v, _ := new(big.Int).SetString(strLn10[:1]+strLn10[2:digits+2], 10) - v.Lsh(v, b) - return v.Quo(v, pow10(int64(digits))), true -} - -// roundBounds rounds v, known only by lo <= v*10^scale <= hi (lo >= 0), half away from zero to -// precision places after the decimal point, or to max(precision, 1) significant digits when that -// would be 0. It reports false when lo and hi do not round to the same value. -func roundBounds(lo, hi *big.Int, scale int64, precision int32) (Decimal, bool) { - g := scale - int64(precision) - if g < 1 { - return Decimal{}, false - } - dhi := int64(Decimal{value: hi}.NumDigits()) - // with fewer than g digits hi rounds to 0, skip building 10^g for tiny results - if g <= dhi { - if q := roundHalfUp(hi, g); q.Sign() != 0 { - return Decimal{q, -precision}, q.Cmp(roundHalfUp(lo, g)) == 0 - } - } - - sig := powMinSig(precision) - g = dhi - sig - if g < 2 { - return Decimal{}, false - } - q := roundHalfUp(hi, g) - res := Decimal{q, int32(g - scale)} - if int64(Decimal{value: lo}.NumDigits()) == dhi { - return res, q.Cmp(roundHalfUp(lo, g)) == 0 - } - // lo < 10^(dhi-1) <= hi, decided only when both round to that power of ten - return res, q.Cmp(pow10(sig-1)) == 0 && roundHalfUp(lo, g-1).Cmp(pow10(sig)) == 0 -} - -// roundHalfUp returns x / 10^g rounded half away from zero, for x >= 0 and g >= 1. -func roundHalfUp(x *big.Int, g int64) *big.Int { - p := pow10(g) - q, r := new(big.Int).QuoRem(x, p, new(big.Int)) - if r.Lsh(r, 1).Cmp(p) >= 0 { - q.Add(q, oneInt) - } - return q -} - -// powSigDigits returns the number of significant digits of a result of magnitude 10^lg rounded to -// precision places, at least powMinSig(precision). -func powSigDigits(lg float64, precision int32) int64 { - if n := int64(math.Floor(lg)) + 1 + int64(precision); n > powMinSig(precision) { - return n - } - return powMinSig(precision) -} - -// powMinSig returns the significant digits kept by results that would round to zero. -func powMinSig(precision int32) int64 { - if precision < 1 { - return 1 - } - return int64(precision) -} - -// powInRange reports whether a result of magnitude 10^lg rounded to precision places has an exponent -// that fits into int32. -func powInRange(lg float64, precision int32) bool { - return math.Abs(lg)+math.Abs(float64(precision)) < math.MaxInt32/2 -} - -// absIsOne reports whether |d| = 1. -func (d Decimal) absIsOne() bool { - nd := int64(d.NumDigits()) - return nd-1+int64(d.exp) == 0 && d.getValue().CmpAbs(pow10(nd-1)) == 0 -} - -// log10Abs approximates log10(|d|) for d != 0. -func (d Decimal) log10Abs() float64 { - var m big.Float - e2 := new(big.Float).SetInt(d.getValue()).MantExp(&m) - mf, _ := m.Float64() - return math.Log10(math.Abs(mf)) + float64(e2)*math.Log10(2) + float64(d.exp) -} - -func abs64(n int64) int64 { - if n < 0 { - return -n - } - return n -} - -// Sqrt returns the square root of d with DivisionPrecision digits after the decimal point, like Div. -// Use SqrtRound to choose the precision per call. It returns an error for negative d. -// -// Example: -// -// d, err := NewFromInt(2).Sqrt() -// d.String() // output: "1.414213562373095" -func (d Decimal) Sqrt() (Decimal, error) { - return d.SqrtRound(int32(DivisionPrecision)) -} - -// SqrtRound returns the square root of d rounded half away from zero to places digits after the -// decimal point, like DivRound. The result is exact up to that rounding. It returns an error for -// negative d. -// -// Example: -// -// d, err := NewFromFloat(0.002342).SqrtRound(4) -// d.String() // output: "0.0484" -func (d Decimal) SqrtRound(places int32) (Decimal, error) { - if d.Sign() < 0 { - return Decimal{}, fmt.Errorf("cannot represent imaginary value of sqrt(x), where x < 0") - } - - // floor(sqrt(d) * 10^(places+1)) is the integer square root of d * 10^(2*places+2), and its - // last digit decides the rounding. Below 1 that product truncates to an integer first, which - // doesn't change the floor, and when it's below 1 the result is 0. - k := int64(d.exp) + 2*int64(places) + 2 - n := new(big.Int) - if k >= 0 { - n.Mul(d.getValue(), pow10(k)) - } else if -k <= int64(d.NumDigits()) { - n.Quo(d.getValue(), pow10(-k)) - } - n.Sqrt(n) - n.Add(n, fiveInt).Quo(n, tenInt) - return Decimal{value: n, exp: -places}, nil -} - -// Root returns the n-th root of d with DivisionPrecision digits after the decimal point, like Div. -// Use RootRound to choose the precision per call. Odd roots of negative numbers are negative. -// It returns an error when n < 1, and for negative d when n is even. -// -// Example: -// -// d, err := NewFromInt(2).Root(3) -// d.String() // output: "1.2599210498948732" -func (d Decimal) Root(n int64) (Decimal, error) { - return d.RootRound(n, int32(DivisionPrecision)) -} - -// RootRound returns the n-th root of d rounded half away from zero to places digits after the -// decimal point, like DivRound. The result is exact up to that rounding. Odd roots of negative -// numbers are negative. It returns an error when n < 1, and for negative d when n is even. -// The work grows with n times places, so very large roots such as the millionth are slow. -// -// Example: -// -// d, err := NewFromFloat(-0.125).RootRound(3, 2) -// d.String() // output: "-0.5" -func (d Decimal) RootRound(n int64, places int32) (Decimal, error) { - if n < 1 { - return Decimal{}, fmt.Errorf("cannot calculate the %d-th root, n must be at least 1", n) - } - if d.Sign() < 0 && n%2 == 0 { - return Decimal{}, fmt.Errorf("cannot represent imaginary value of an even root of x, where x < 0") - } - - // floor(root(|d|) * 10^(places+1)) is the integer n-th root of |d| * 10^(n*(places+1)), and its - // last digit decides the rounding, as in SqrtRound. - k := int64(d.exp) + n*(int64(places)+1) - x := new(big.Int).Abs(d.getValue()) - if k >= 0 { - x.Mul(x, pow10(k)) - } else if -k <= int64(d.NumDigits()) { - x.Quo(x, pow10(-k)) - } else { - x.SetInt64(0) - } - r := new(big.Int) - if x.Sign() > 0 { - root, _ := iroot(x, n) - r.Set(root) - } - r.Add(r, fiveInt).Quo(r, tenInt) - if d.Sign() < 0 { - r.Neg(r) - } - return Decimal{value: r, exp: -places}, nil -} - -// ExpHullAbrham calculates the natural exponent of decimal (e to the power of d) using Hull-Abraham algorithm. -// OverallPrecision argument specifies the overall precision of the result (integer part + decimal part). -// -// ExpHullAbrham is faster than ExpTaylor for small precision values, but it is much slower for large precision values. -// -// Example: -// -// d1, err := NewFromFloat(26.1).ExpHullAbrham(2) -// d1.String() // output: "220000000000" -// -// d2, err := NewFromFloat(26.1).ExpHullAbrham(20) -// d2.String() // output: "216314672147.05767284" -func (d Decimal) ExpHullAbrham(overallPrecision uint32) (Decimal, error) { - // Algorithm based on Variable precision exponential function. - // ACM Transactions on Mathematical Software by T. E. Hull & A. Abrham. - if d.IsZero() { - return Decimal{oneInt, 0}, nil - } - - currentPrecision := overallPrecision - - // Algorithm does not work if currentPrecision * 23 < |x|. - // Precision is automatically increased in such cases, so the value can be calculated precisely. - // If newly calculated precision is higher than ExpMaxIterations the currentPrecision will not be changed. - f := d.Abs().InexactFloat64() - if ncp := f / 23; ncp > float64(currentPrecision) && ncp < float64(ExpMaxIterations) { - currentPrecision = uint32(math.Ceil(ncp)) - } - - // fail if abs(d) beyond an over/underflow threshold - overflowThreshold := New(23*int64(currentPrecision), 0) - if d.Abs().Cmp(overflowThreshold) > 0 { - return Decimal{}, fmt.Errorf("over/underflow threshold, exp(x) cannot be calculated precisely") - } - - // Return 1 if abs(d) small enough; this also avoids later over/underflow - overflowThreshold2 := New(9, -int32(currentPrecision)-1) - if d.Abs().Cmp(overflowThreshold2) <= 0 { - return Decimal{oneInt, 0}, nil - } - - // t is the smallest integer >= 0 such that the corresponding abs(d/k) < 1 - t := d.exp + int32(d.NumDigits()) // Add d.NumDigits because the paper assumes that d.value [0.1, 1) - - if t < 0 { - t = 0 - } - - k := New(1, t) // reduction factor - r := Decimal{new(big.Int).Set(d.getValue()), d.exp - t} // reduced argument - p := int32(currentPrecision) + t + 2 // precision for calculating the sum - - // Determine n, the number of therms for calculating sum - // use first Newton step (1.435p - 1.182) / log10(p/abs(r)) - // for solving appropriate equation, along with directed - // roundings and simple rational bound for log10(p/abs(r)) - rf := r.Abs().InexactFloat64() - pf := float64(p) - nf := math.Ceil((1.453*pf - 1.182) / math.Log10(pf/rf)) - if nf > float64(ExpMaxIterations) || math.IsNaN(nf) { - return Decimal{}, fmt.Errorf("exact value cannot be calculated in <=ExpMaxIterations iterations") - } - n := int64(nf) - - tmp := New(0, 0) - sum := New(1, 0) - one := New(1, 0) - for i := n - 1; i > 0; i-- { - tmp.value.SetInt64(i) - sum = sum.Mul(r.DivRound(tmp, p)) - sum = sum.Add(one) - } - - // res = sum^ki, the same value ki repeated multiplications would give - ki := k.IntPart() - expInt64 := int64(sum.exp) * ki - if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 { - panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64)) - } - res := Decimal{new(big.Int).Exp(sum.getValue(), big.NewInt(ki), nil), int32(expInt64)} - - // Round to overallPrecision significant figures, at least 1. A raised currentPrecision only keeps - // the calculation precise, it doesn't add digits to the result. - sig := int32(overallPrecision) - if sig == 0 { - sig = 1 - } - roundDigits := sig - int32(res.NumDigits()) - res.exp - - res = res.Round(roundDigits) - - return res, nil -} - -// ExpTaylor calculates the natural exponent of decimal (e to the power of d) using Taylor series expansion. -// Precision argument specifies how precise the result must be (number of digits after decimal point). -// Negative precision is allowed. -// -// ExpTaylor is much faster for large precision values than ExpHullAbrham. -// -// Example: -// -// d1, err := NewFromFloat(26.1).ExpTaylor(2) -// d1.String() // output: "216314672147.06" -// -// d2, err := NewFromFloat(26.1).ExpTaylor(20) -// d2.String() // output: "216314672147.05767284062928674083" -// -// d3, err := NewFromFloat(26.1).ExpTaylor(-10) -// d3.String() // output: "220000000000" -func (d Decimal) ExpTaylor(precision int32) (Decimal, error) { - // Note(mwoss): Implementation can be optimized by exclusively using big.Int API only - if d.IsZero() { - return Decimal{oneInt, 0}.Round(precision), nil - } - - var epsilon Decimal - var divPrecision int32 - if precision < 0 { - epsilon = New(1, -1) - divPrecision = 8 - } else { - epsilon = New(1, -precision-1) - divPrecision = precision + 1 - } - - decAbs := d.Abs() - pow := d.Abs() - factorial := New(1, 0) - - result := New(1, 0) - - for i := int64(1); ; { - step := pow.DivRound(factorial, divPrecision) - result = result.Add(step) - - // Stop Taylor series when current step is smaller than epsilon - if step.Cmp(epsilon) < 0 { - break - } - - pow = pow.Mul(decAbs) - - i++ - - // Calculate next factorial number or retrieve cached value - factorialsMutex.RLock() - if len(factorials) >= int(i) && !factorials[i-1].IsZero() { - factorial = factorials[i-1] - factorialsMutex.RUnlock() - } else { - prevFactorial := factorials[i-2] - factorialsMutex.RUnlock() - factorial = prevFactorial.Mul(New(i, 0)) - factorialsMutex.Lock() - // Check again in case another goroutine already added it. - if len(factorials) < int(i) || factorials[i-1].IsZero() { - factorials = append(factorials, Zero) - factorials[i-1] = factorial - } - factorialsMutex.Unlock() - } - } - - if d.Sign() < 0 { - result = New(1, 0).DivRound(result, precision+1) - } - - result = result.Round(precision) - 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. The result is correctly rounded half away from zero. -// -// 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) { - 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") - } - - // d = m * 10^k with 1 <= m < 10, so ln(d) = ln(m) + k*ln(10). Both are computed with error bounds, - // and the working precision grows until the whole interval rounds to the same value. That ends, - // because ln(d) is irrational for every d other than 1. - k := int64(d.NumDigits()) - 1 + int64(d.exp) - f := int64(precision) + 10 - if f < 10 { - f = 10 - } - for ; ; f += 20 { - b := uint(float64(f)*math.Log2(10)) + uint(bits.Len64(uint64(abs64(k)))) + 8 - m := new(big.Int).Lsh(d.getValue(), b) - m.Quo(m, pow10(int64(d.NumDigits())-1)) - t, lnErr := lnFixed(m, b) - ln10, ln10Err := ln10Fixed(b) - t.Add(t, new(big.Int).Mul(big.NewInt(k), ln10)) - tErr := big.NewInt(lnErr + 1 + abs64(k)*ln10Err) - - neg := t.Sign() < 0 - t.Abs(t) - p := pow10(f) - hi := new(big.Int).Add(t, tErr) - hi.Mul(hi, p).Rsh(hi, b).Add(hi, oneInt) - lo := t.Sub(t, tErr) - if lo.Sign() < 0 { - lo.SetInt64(0) - } - lo.Mul(lo, p).Rsh(lo, b) - - g := f - int64(precision) - if q := roundHalfUp(hi, g); q.Cmp(roundHalfUp(lo, g)) == 0 { - if neg { - q.Neg(q) - } - return Decimal{value: q, exp: -precision}, nil - } - } -} - -// Trig functions - -var ( - // Pi/4 split into three parts - pi4A = NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000 - pi4B = NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000 - pi4C = NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170 - m4PI = NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi - - atanP0 = NewFromFloat(-8.750608600031904122785e-01) - atanP1 = NewFromFloat(-1.615753718733365076637e+01) - atanP2 = NewFromFloat(-7.500855792314704667340e+01) - atanP3 = NewFromFloat(-1.228866684490136173410e+02) - atanP4 = NewFromFloat(-6.485021904942025371773e+01) - atanQ0 = NewFromFloat(2.485846490142306297962e+01) - atanQ1 = NewFromFloat(1.650270098316988542046e+02) - atanQ2 = NewFromFloat(4.328810604912902668951e+02) - atanQ3 = NewFromFloat(4.853903996359136964868e+02) - atanQ4 = NewFromFloat(1.945506571482613964425e+02) - - atanMorebits = NewFromFloat(6.123233995736765886130e-17) // pi/2 = PIO2 + Morebits - tan3pio8 = NewFromFloat(2.41421356237309504880) // tan(3*pi/8) - piFloat = NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459) -) - -// Atan returns the arctangent, in radians, of x. -func (d Decimal) Atan() Decimal { - if d.IsZero() { - return d - } - if d.IsPositive() { - return d.satan() - } - return d.Neg().satan().Neg() -} - -func (d Decimal) xatan() Decimal { - P0, P1, P2, P3, P4 := atanP0, atanP1, atanP2, atanP3, atanP4 - Q0, Q1, Q2, Q3, Q4 := atanQ0, atanQ1, atanQ2, atanQ3, atanQ4 - z := d.Mul(d) - b1 := P0.Mul(z).Add(P1).Mul(z).Add(P2).Mul(z).Add(P3).Mul(z).Add(P4).Mul(z) - b2 := z.Add(Q0).Mul(z).Add(Q1).Mul(z).Add(Q2).Mul(z).Add(Q3).Mul(z).Add(Q4) - z = b1.Div(b2) - z = d.Mul(z).Add(d) - return z -} - -// satan reduces its argument (known to be positive) -// to the range [0, 0.66] and calls xatan. -func (d Decimal) satan() Decimal { - Morebits, Tan3pio8, pi := atanMorebits, tan3pio8, piFloat - - if d.LessThanOrEqual(New(66, -2)) { - return d.xatan() - } - if d.GreaterThan(Tan3pio8) { - return pi.Div(New(2, 0)).Sub(New(1, 0).Div(d).xatan()).Add(Morebits) - } - return pi.Div(New(4, 0)).Add((d.Sub(New(1, 0)).Div(d.Add(New(1, 0)))).xatan()).Add(New(5, -1).Mul(Morebits)) -} - -// sin coefficients -var _sin = [...]Decimal{ - NewFromFloat(1.58962301576546568060e-10), // 0x3de5d8fd1fd19ccd - NewFromFloat(-2.50507477628578072866e-8), // 0xbe5ae5e5a9291f5d - NewFromFloat(2.75573136213857245213e-6), // 0x3ec71de3567d48a1 - NewFromFloat(-1.98412698295895385996e-4), // 0xbf2a01a019bfdf03 - NewFromFloat(8.33333333332211858878e-3), // 0x3f8111111110f7d0 - NewFromFloat(-1.66666666666666307295e-1), // 0xbfc5555555555548 -} - -// Sin returns the sine of the radian argument x. -func (d Decimal) Sin() Decimal { - PI4A, PI4B, PI4C, M4PI := pi4A, pi4B, pi4C, m4PI - - if d.IsZero() { - return d - } - // make argument positive but save the sign - sign := false - if d.IsNegative() { - d = d.Neg() - sign = true - } - - j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle - y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float - - // map zeros to origin - if j&1 == 1 { - j++ - y = y.Add(New(1, 0)) - } - j &= 7 // octant modulo 2Pi radians (360 degrees) - // reflect in x axis - if j > 3 { - sign = !sign - j -= 4 - } - z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic - zz := z.Mul(z) - - if j == 1 || j == 2 { - w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) - y = New(1, 0).Sub(New(5, -1).Mul(zz)).Add(w) - } else { - y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) - } - if sign { - y = y.Neg() - } - return y -} - -// cos coefficients -var _cos = [...]Decimal{ - NewFromFloat(-1.13585365213876817300e-11), // 0xbda8fa49a0861a9b - NewFromFloat(2.08757008419747316778e-9), // 0x3e21ee9d7b4e3f05 - NewFromFloat(-2.75573141792967388112e-7), // 0xbe927e4f7eac4bc6 - NewFromFloat(2.48015872888517045348e-5), // 0x3efa01a019c844f5 - NewFromFloat(-1.38888888888730564116e-3), // 0xbf56c16c16c14f91 - NewFromFloat(4.16666666666665929218e-2), // 0x3fa555555555554b -} - -// Cos returns the cosine of the radian argument x. -func (d Decimal) Cos() Decimal { - - PI4A, PI4B, PI4C, M4PI := pi4A, pi4B, pi4C, m4PI - - // make argument positive - sign := false - if d.IsNegative() { - d = d.Neg() - } - - j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle - y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float - - // map zeros to origin - if j&1 == 1 { - j++ - y = y.Add(New(1, 0)) - } - j &= 7 // octant modulo 2Pi radians (360 degrees) - // reflect in x axis - if j > 3 { - sign = !sign - j -= 4 - } - if j > 1 { - sign = !sign - } - - z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic - zz := z.Mul(z) - - if j == 1 || j == 2 { - y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5]))) - } else { - w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5])) - y = New(1, 0).Sub(New(5, -1).Mul(zz)).Add(w) - } - if sign { - y = y.Neg() - } - return y -} - -var _tanP = [...]Decimal{ - NewFromFloat(-1.30936939181383777646e+4), // 0xc0c992d8d24f3f38 - NewFromFloat(1.15351664838587416140e+6), // 0x413199eca5fc9ddd - NewFromFloat(-1.79565251976484877988e+7), // 0xc1711fead3299176 -} -var _tanQ = [...]Decimal{ - NewFromFloat(1.00000000000000000000e+0), - NewFromFloat(1.36812963470692954678e+4), // 0x40cab8a5eeb36572 - NewFromFloat(-1.32089234440210967447e+6), // 0xc13427bc582abc96 - NewFromFloat(2.50083801823357915839e+7), // 0x4177d98fc2ead8ef - NewFromFloat(-5.38695755929454629881e+7), // 0xc189afe03cbe5a31 -} - -// Tan returns the tangent of the radian argument x. -func (d Decimal) Tan() Decimal { - - PI4A, PI4B, PI4C, M4PI := pi4A, pi4B, pi4C, m4PI - - if d.IsZero() { - return d - } - - // make argument positive but save the sign - sign := false - if d.IsNegative() { - d = d.Neg() - sign = true - } - - j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle - y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float - - // map zeros to origin - if j&1 == 1 { - j++ - y = y.Add(New(1, 0)) - } - - z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic - zz := z.Mul(z) - - if zz.GreaterThan(New(1, -14)) { - w := zz.Mul(_tanP[0].Mul(zz).Add(_tanP[1]).Mul(zz).Add(_tanP[2])) - x := zz.Add(_tanQ[1]).Mul(zz).Add(_tanQ[2]).Mul(zz).Add(_tanQ[3]).Mul(zz).Add(_tanQ[4]) - y = z.Add(z.Mul(w.Div(x))) - } else { - y = z - } - if j&2 == 2 { - y = New(-1, 0).Div(y) - } - if sign { - y = y.Neg() - } - return y -} diff --git a/rounding.go b/rounding.go new file mode 100644 index 0000000..8008f55 --- /dev/null +++ b/rounding.go @@ -0,0 +1,119 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Multiprecision decimal numbers. +// For floating-point formatting only; not general purpose. +// Only operations are assign and (binary) left/right shift. +// Can do binary floating point in multiprecision decimal precisely +// because 2 divides 10; cannot do decimal floating point +// in multiprecision binary precisely. + +package decimal + +type floatInfo struct { + mantbits uint + expbits uint + bias int +} + +var float32info = floatInfo{23, 8, -127} +var float64info = floatInfo{52, 11, -1023} + +// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely reconstructed. +func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { + // If mantissa is zero, the number is zero; stop now. + if mant == 0 { + d.nd = 0 + return + } + + // Compute upper and lower such that any decimal number + // between upper and lower (possibly inclusive) + // will round to the original floating point number. + + // We may see at once that the number is already shortest. + // + // Suppose d is not denormal, so that 2^exp <= d < 10^dp. + // The closest shorter number is at least 10^(dp-nd) away. + // The lower/upper bounds computed below are at distance + // at most 2^(exp-mantbits). + // + // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits), + // or equivalently log2(10)*(dp-nd) > exp-mantbits. + // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32). + minexp := flt.bias + 1 // minimum possible exponent + if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) { + // The number is already shortest. + return + } + + // d = mant << (exp - mantbits) + // Next highest floating point number is mant+1 << exp-mantbits. + // Our upper bound is halfway between, mant*2+1 << exp-mantbits-1. + upper := new(decimal) + upper.Assign(mant*2 + 1) + upper.Shift(exp - int(flt.mantbits) - 1) + + // d = mant << (exp - mantbits) + // Next lowest floating point number is mant-1 << exp-mantbits, + // unless mant-1 drops the significant bit and exp is not the minimum exp, + // in which case the next lowest is mant*2-1 << exp-mantbits-1. + // Either way, call it mantlo << explo-mantbits. + // Our lower bound is halfway between, mantlo*2+1 << explo-mantbits-1. + var mantlo uint64 + var explo int + if mant > 1<