Giter Site home page Giter Site logo

ericlagergren / decimal Goto Github PK

View Code? Open in Web Editor NEW
502.0 15.0 62.0 1.2 MB

A high-performance, arbitrary-precision, floating-point decimal library.

Home Page: https://godoc.org/github.com/ericlagergren/decimal

License: BSD 3-Clause "New" or "Revised" License

Go 99.02% Ragel 0.96% Shell 0.02%
decimal arbitrary-precision dogs-of-instagram general-decimal-arithmetic money big-decimal multi-precision financial data-science

decimal's Introduction

decimal Build Status GoDoc

decimal implements arbitrary precision, decimal floating-point numbers, per the General Decimal Arithmetic specification.

Features

  • Useful zero values. The zero value of a decimal.Big is 0, just like math/big.

  • Multiple operating modes. Different operating modes allow you to tailor the package's behavior to your needs. The GDA mode strictly implements the GDA specification, while the Go mode implements familiar Go idioms.

  • High performance. decimal is consistently one of the fastest arbitrary-precision decimal floating-point libraries, regardless of language.

  • An extensive math library. The math/ subpackage implements elementary and trigonometric functions, continued fractions, and more.

  • A familiar, idiomatic API. decimal's API follows math/big's API, so there isn't a steep learning curve.

Installation

go get github.com/ericlagergren/decimal

Documentation

GoDoc

Versioning

decimal uses Semantic Versioning. The current version is 3.3.1.

decimal only explicitly supports the two most recent major Go 1.X versions.

License

BSD 3-clause

decimal's People

Contributors

ab0455a08d03 avatar alexeykiselev avatar atrox avatar chochihim avatar dvrkps avatar ericlagergren avatar fantomgs avatar hanfezh avatar kardianos avatar kbespalov avatar kjgorman avatar logank avatar lopezator avatar lotodore avatar maddyblue avatar mindreframer avatar nathanhack avatar rdingwall avatar schumacherfm avatar timothyham avatar yarco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

decimal's Issues

rounding with lossless operations

Some operations like addition can be performed without loss of precision. What are your thoughts about instead respecting the context's precision during these operations, which would round after all operations? A precision of 0 would keep the current behavior to not round.

For example, with a precision of 3:

100 + 0.1 = 100

Benefits to this approach:

  • Some algorithms for computing pow involve repeated squaring. Given an already large input, after ~10 iterations the number of digits is crazy high and it takes a few seconds per multiplication. Restricting these operations to some known precision can prevent this kind of problem. (See cockroachdb/cockroach@b7c11c8 for example.)
  • The test suite at speleotrove requires this, and I think would make a good addition to this package.

go get fails

Is this package considered stable?

..\..\..\github.com\ericlagergren\decimal\big.go:765: xb.IsInt64 undefined (type *big.Int has no field or method IsInt64)

Negative Cmp

I have this new issue, Cmp should return -1, right?

func TestCmpNeg(t *testing.T) {
	x := New(-20102859, 6)
	y := New(-20, 0)
	if x.Cmp(y) != -1 {
		t.Errorf("error in cmp x=%v, y=%v, x.Cmp(y)=%v", x, y, x.Cmp(y))
	}
}
=> error in cmp x=-20.102859, y=-20, x.Cmp(y)=1

Missing basic functionality

I believe the following basic functionality should be provided:


From #5:

I think the library should provide the following functionality:

To String

From String

"suite" tests

There are many tests in the suite directory. But I can't find where they are actually tested. Was your work there preliminary?

Also what are your thoughts on http://speleotrove.com/decimal/ which appears to be a similar project from IBM, but with other coverage?

Int64

I have another one to report (hope still a quick one). Int64 fails but if we do Int().Int64() than it's OK:

func TestDecInt64(t *testing.T) {
	x := New(10240000000000, 0)
	x.Mul(x, New(976563, 9))
	if x.Int64() != 10000005120 {
		t.Error("error int64: ", x.Int64(), x.Int().Int64())
	}
}
decimal_test.go:187: error int64:  -844673895 10000005120

Round to a different scale?

Hello! Apologies if this is a very basic question, but I'm writing a financial application and trying to use decimal.Big to round an amount $0.009 to the nearest whole penny ($0.01). In this case the precision stays the same (1) but the scale changes (from 3 to 2).

v, _ := decimal.New(0, 0).SetString("0.009") // 0.009, scale 3, precision 1

v.Round(something) // 0.01, scale 2, precision 1

I'm not quite sure how to achieve this because big.Round() only takes a parameter n for the new desired precision (which cannot be zero or negative).

Am I being terribly stupid here?

Bad Cmp result

Hi, I just switched from doing floating point to you excellent decimal lib.

In the test I reached a point where I have this:
t.Log(result.Cost.Cmp(expected.Cost), expected.Cost.String(), result.Cost.String())

with this wrong result:
1 2701 2701
The expected cost is created as decimal.New(2701, 0) and the result comes from the tested function.
If you need other info please let me know!

Thanks,
-rif

verify 32-bit support

Right now the tests are set up for 64-bit platforms (the min/max exponents in the test suite are too large for 32-bit).

All the tests "pass" with GOARCH=386 to the extent that the arithmetic returns correct results. The signs, exponents, and such are wonky and incorrect because the test suite assumes the results can be larger than they're supposed to be.

Possibly not needed functionlity

Generally I think that a library should provide only one way of doing something. Also more functionality means more tests, more maintenance effort, more bugs, etc. So I will list methods that I think you can safely remove from this library (they can always be re-added later):

  • GreaterThan, GreaterThanEQ, LessThan, LessThanEQ, Equals - Cmp should be enough
  • Binomial, Fib, MulRange, Jacobi. This probably should be in another package or just use big.Int. And FizzBuzz too! ;)
  • Hypot, Log, Log10. This probably should be in another package or just use big.Float
  • MarshalJSON, UnmarshalJSON. MarshalText/UnmarshalText are already used by encoding/json
  • BitLen. Probably not needed, the documentation is confusing. What are the use cases?
  • Bytes, SetBytes Probably not needed. How is it different from GobEncode/GobDecode?
  • Ilog10. Why round before taking log10? What is the use case for this method?
  • What is the difference between Rem and Mod? Maybe just documentation issue
  • sql.Scanner and driver.Value implementations are probably not needed. Storing decimals in databases is database specific.

Feedback on names

I have only looked at the docs so far. My initial feedback is on naming. I think some types/methods could be named better. This is quite subjective of course but I will try to explain my choices.

  • decimal.Decimal. Is one decimal in the type name not enough? I think decimal.Number or decimal.Big would be a better name. This also leaves the possibility to add fixed precision decimal types to this packge using names such as decimal.Single, decimal.Double, decimal.Quad).
  • DefaultPrecision and MaxPrec/MinPrec are not consistently named.
  • Variable names for pre-defined contexts (such as decimal.Decimal32) could be better named. How?
  • RoundingMode. Maybe you can use math/big.RoundingMode or at least the same names?
  • NewFromXXX methods. Maybe you can follow big.Float more closely and provide SetXX methods instead? E.g.: https://golang.org/pkg/math/big/#Int.SetString. In general I think following big.Float is a good idea as it makes the package easier to learn.
  • Scientific. Better call it ScientificString or just String. I will provide more feedback on to/from string conversion later.

This list is not complete. I will add more comments later.

se of internal package not allowed

for some reason I get

../../go/src/github.com/EricLagergren/decimal/decimal.go:45:2: use of internal package not allowed
../../go/src/github.com/EricLagergren/decimal/decimal.go:46:2: use of internal package not allowed
../../go/src/github.com/EricLagergren/decimal/decimal.go:47:2: use of internal package not allowed
../../go/src/github.com/EricLagergren/decimal/decimal.go:48:2: use of internal package not allowed

Explore x.unscaled.BitLen() / log10(2) for x.Precision() when !x.isCompact()

There currently exists a hard limit of a precision of 4294967295 because our algorithm overflows if x.BitLen() > 14267572532, where x is the unscaled value of a non-compact Big decimal.

There are multiple ways to calculate the precision using iteration, but it'd be nice if float64(x.BitLen()) / (log10/log2)) provided a reasonable-enough value we could use it for instances where we have more than 4294967295 digits of precision.

Float64()

Is a method like (*Big) Float64() float64 out of the question?
I would use that mostly for formatting using strconv.FormatFloat(). Currently to use that I would use String() than ParseFloat than FormatFloat.
Is there an easier workaround?

Question about copy and how it affects string output

Below is a small example and it's output

package main

import (
	"fmt"

	"github.com/ericlagergren/decimal"
)

const (
	precision = 24
	scale     = 12
)

var defaultCxt = decimal.Context{
	OperatingMode: decimal.Go,
	Precision:     precision,
	RoundingMode:  decimal.ToNearestEven,
	Traps:         ^(decimal.Inexact | decimal.Rounded | decimal.Subnormal),
}

func main() {
	tmp := decimal.WithContext(defaultCxt)
	tmp.SetString("0.0")
	fmt.Println(new(decimal.Big).Copy(tmp).Quantize(scale).String())
	fmt.Println(tmp.Quantize(scale).String())
}
0E-12
0

I was wondering why I'm seeing a scientific notation when I do a copy?

Thanks

Question about quantize and rounding modes

I'm trying to understand the Banker's round mode and how quantize() works. Below is my example

package main

import "github.com/ericlagergren/decimal"
import "fmt"

func main() {
	x := decimal.WithContext(decimal.Context{
		OperatingMode: decimal.Go,
		Precision:     24,
		RoundingMode:  decimal.ToNearestEven,
		Traps:         ^(decimal.Inexact | decimal.Rounded | decimal.Subnormal),
	})
	x.SetFloat64(2.335)
	fmt.Println(x.Quantize(2))
	x.SetFloat64(2.325)
	fmt.Println(x.Quantize(2))
}

I would expect this to produce

2.34
2.32

but instead I get

2.33
2.33

Is this expected? Am I missing something?

Thanks

Lint and cyclo errors

Hey there, was just looking at this library as something to include in sqlboiler for decimal support instead of shopspring/decimal (they have some really scary issues still opened). Do you think it'd be a good fit for something like that? Mostly users would be using it for financial dealings. I'm going to file another issue on the lack of marshallers on this project too, I have a feeling that the encoding/Text* interfaces don't work for as many things as you might want them to.

As for the issue at hand, there's quite a number of linting errors in the lib:
https://goreportcard.com/report/github.com/ericlagergren/decimal

Quo generates incorrect results for small division

// +build ignore

package main

import (
"fmt"

"github.com/EricLagergren/decimal"

)

func main() {
v1 := decimal.New(1, 0)
v2 := decimal.New(10, 0)
v3 := decimal.New(100, 0)

v2.Quo(v1, v2) // 1 / 10 == 0.1
v3.Quo(v3, v1) // 100 / 1 == 100
fmt.Println(v2, v3)

}

String() method is slow

I've been using shopspring/decimal until now and I've recently switched to this one. Quick benchmarks show that my apps arithmetic related methods just got faster 3 times. (Thanks!) However after all the operations are finished I'm writing this data in bulk to an SQL database and at that point conversion to string via big.String() is 2 times slower compared to shopspring's decimal library.

Is there a quick fix? I've been thinking if there was an easy way to extract the numbers after and before the decimal point, it would be quite fast to form something like "-xx.yyy" but couldn't find such functionality.

thanks

You readMe usage/example has an error.

You should replace:
...Quo(total, preTax)
by,
....Div(total, preTax)

See last line of the example:
fmt.Println("Tax rate:", new(decimal.Decimal).Sub(total, preTax).Quo(total, preTax)

Compilation errors on tip (251aad2fc906ece085f121a463eb28a0ee6fda6f)

go get -u github.com/ericlagergren/decimal
# github.com/ericlagergren/decimal
/opt/gopath/src/github.com/ericlagergren/decimal/decimal.go:913:39: cannot convert x (type *Big) to type *Big
/opt/gopath/src/github.com/ericlagergren/decimal/decimal.go:933:17: cannot use f.w.(*compat.Builder) (type *compat.Builder) as type io.Reader in argument to io.Copy:
        *compat.Builder does not implement io.Reader (missing Read method)
/opt/gopath/src/github.com/ericlagergren/decimal/decimal.go:1146:10: b.Bytes undefined (type compat.Builder has no field or method Bytes)
/opt/gopath/src/github.com/ericlagergren/decimal/decimal.go:1999:30: cannot convert x (type *Big) to type *Big
go version devel +36ef06cc10 Fri Nov 10 18:17:38 2017 +0000 linux/amd64

Seems to be failing on Travis as well.

log, exp

What's the status of log and exp? They are commented out. Are they broken, not sufficiently tested, too slow?

trig functions

The usual suspects are really needed.
cos, sin, tan, arccos, arcsin, arctan, and atan2.

To/From string conversion

I think the library should provide the following functionality:

To String

From String

Error handling (panics, nan, inf)

I think that a library like decimal should never panic. Generally inputs can come from users (e.g. from an HTML form) and it is much nicer to have an ability to handle errors without using recover or pre-validating decimals before each operation.

Have a look at math/big.Accuracy, I think it is better to use it instead of bool or error where it makes sense. E.g. https://golang.org/pkg/math/big/#Float.Uint64

It might be good idea to introduce +Inf and -Inf values for Decimal objects. This gives natural way to report errors for many operations, such as multiplying two large numbers, division by 0, parsing something like 1e+908940390904920390109201902909201901, etc.

  • Trade panics for +/- Inf
  • Return something similar to big.Accuracy

Should IsBig be replaced with IsUint64, IsInt64?

I've found myself wanting to check whether x can fit in an int64 or uint64 (the former more than the latter), rather than whether it's resigned to a big.Int.

This pattern appears a lot:

if x.IsInt() && !x.IsBig() { /* handle x as an int64 or uint64 */ }

It might be worth dropping IsBig and replacing it with IsInt64 and IsUint64.

problem while go get decimal the way the readme suggests.

I got a recent go 1.7.4

go get github.com/EricLagergren/decimal

resolves into the following message

package github.com/EricLagergren/decimal
	imports github.com/ericlagergren/decimal/internal/arith: use of internal package not allowed
package github.com/EricLagergren/decimal
	imports github.com/ericlagergren/decimal/internal/arith/checked: use of internal package not allowed
package github.com/EricLagergren/decimal
	imports github.com/ericlagergren/decimal/internal/arith/pow: use of internal package not allowed
package github.com/EricLagergren/decimal
	imports github.com/ericlagergren/decimal/internal/c: use of internal package not allowed

the way it works is

go get github.com/ericlagergren/decimal

please update your readme

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.