Giter Site home page Giter Site logo

fpmoney's Introduction

🧧 Fixed-Point Decimal Money

codecov Go Report Card Go Reference Mentioned in Awesome Go OpenSSF Scorecard

Be Precise: using floats to represent currency is almost criminal. — Robert.C.Martin, "Clean Code" p.301

  • as fast as int64
  • no float in parsing nor printing, does not leak precision
  • ISO 4217 currency
  • block mismatched currency arithmetics
  • 100 LOC
  • fuzz tests
var BuySP500Price = fpmoney.FromInt(9000, fpmoney.SGD)

input := []byte(`{"sp500": {"amount": 9000.02, "currency": "SGD"}}`)

type Stonks struct {
    SP500 fpmoney.Amount `json:"sp500"`
}
var v Stonks
if err := json.Unmarshal(input, &v); err != nil {
    log.Fatal(err)
}

amountToBuy := fpmoney.FromInt(0, fpmoney.SGD)
if v.SP500.GreaterThan(BuySP500Price) {
    amountToBuy = amountToBuy.Add(v.SP500.Mul(2))
}

fmt.Println(amountToBuy)
// Output: 18000.04 SGD

Ultra Small Fractions

Some denominations have very low fractions. Storing them int64 you would get.

  • BTC satoshi is 1 BTC = 100,000,000 satoshi, which is still enough for ~92,233,720,368 BTC.
  • ETH wei is 1 ETH = 1,000,000,000,000,000,000 wei, which is ~9 ETH. If you deal with wei, you may consider bigint or multiple int64. In fact, official Ethereum code is in Go and it is using bigint (code).

Benchmarks

$ go test -bench=. -benchmem . > fpmoney.bench
$ go test -bench=. -benchmem ./internal/bench/float32 > float32.bench
$ go test -bench=. -benchmem ./internal/bench/int > int.bench
$ benchstat -split="XYZ" int.bench float32.bench fpmoney.bench
name \ time/op              int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-16      383ns ± 0%     408ns ± 0%     294ns ± 0%
JSONUnmarshal/large-16      436ns ± 0%     473ns ± 0%     365ns ± 0%
JSONMarshal/small-16        115ns ± 0%     158ns ± 0%     226ns ± 0%
JSONMarshal/large-16        112ns ± 0%     146ns ± 0%     272ns ± 0%

name \ alloc/op             int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-16       268B ± 0%      270B ± 0%      198B ± 0%
JSONUnmarshal/large-16       272B ± 0%      288B ± 0%      216B ± 0%
JSONMarshal/small-16        57.0B ± 0%     66.0B ± 0%    160.0B ± 0%
JSONMarshal/large-16        72.0B ± 0%     72.0B ± 0%    176.0B ± 0%

name \ allocs/op            int.bench   float32.bench  fpmoney.bench
JSONUnmarshal/small-16       6.00 ± 0%      6.00 ± 0%      3.00 ± 0%
JSONUnmarshal/large-16       6.00 ± 0%      6.00 ± 0%      3.00 ± 0%
JSONMarshal/small-16         2.00 ± 0%      2.00 ± 0%      3.00 ± 0%
JSONMarshal/large-16         2.00 ± 0%      2.00 ± 0%      3.00 ± 0%
goos: darwin
goarch: arm64
pkg: github.com/nikolaydubina/fpmoney
BenchmarkArithmetic/add_x1-16         1000000000	         0.54 ns/op	       0 B/op	       0 allocs/op
BenchmarkArithmetic/add_x100-16       	26382420	        44.42 ns/op	       0 B/op	       0 allocs/op
PASS
ok  	github.com/nikolaydubina/fpmoney	14.200s

References and Related Work

  • ferdypruis/iso4217 was a good inspiration and reference material. it was used in early version as well. it is well maintained and fast library for currencies.
  • github.com/shopspring/decimal: fixed precision; faster printing/parsing/arithmetics; currency handling
  • github.com/Rhymond/go-money: does not use float or interface{} in parsing; currency is enum
  • github.com/ferdypruis/iso4217: skipped deprecated currencies to fit into uint8 and smaller struct size

fpmoney's People

Contributors

dependabot[bot] avatar nikolaydubina 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

Watchers

 avatar  avatar  avatar

fpmoney's Issues

Serialize amount as string

The fpmoney.Amount serializes values to JSON as a floating point amount, and a currency code like this:

{
  "amount": 128.45, 
  "currency": "ARS"
}

That is used in frontends and persisted in databases as a floating point number.

Other currency packages serialize the amount as a string instead, to avoid conversions and lost of precision. For example:

{
  "amount": "128.45", 
  "currency": "ARS"
}

What do you think of having a global option or be customized by the format string stored under the "json" key (or a money key) in the struct field's tag:

type Invoice struct {
    TotalAmount fpmoney.Amount `json:"totalAmount,asString"`
}

I can provide a PR.

Support percent values

Money is usually used in context when a percent is computed on some money value, for example to compute an interest amount.

A Percent type could be added as a companion abstraction for money.

// Percent is a 3 decimal percent value. Internally it is stored as an int64 with a 3 digits scale
//   interestRate := percent.FromStr("3.5")
// It represents a 3.5% (a 0.03500) factor, and stored as 3500
type Percent int64

const Scale = 1000
const ScaledPercentToRate = 100 * Scale

func (p Percent) By(amount fpmoney.Amount) (computed fpmoney.Amount, remainder fpmoney.Amount) {
    return amount.Mul(int(p)).Div(ScaledPercentToRate)
}

Additional methods will be provided.

What do you think?

I can provide a PR.

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.