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
12 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
Some denominations have very low fractions.
Storing them int64
you would get.
BTC
satoshi is1 BTC = 100,000,000 satoshi
, which is still enough for ~92,233,720,368 BTC
.ETH
wei is1 ETH = 1,000,000,000,000,000,000 wei
, which is ~9 ETH
. If you deal with wei, you may considerbigint
or multipleint64
. In fact, official Ethereum code is in Go and it is using bigint (code).
$ go test -bench=. -benchmem .
goos: darwin
goarch: arm64
pkg: github.com/nikolaydubina/fpmoney
cpu: Apple M3 Max
BenchmarkCurrency_UnmarshalText-16 711695404 1.610 ns/op 0 B/op 0 allocs/op
BenchmarkCurrency_AppendText-16 446232057 2.698 ns/op 0 B/op 0 allocs/op
BenchmarkCurrency_MarshalText-16 81956246 13.99 ns/op 8 B/op 1 allocs/op
BenchmarkCurrency_String-16 1000000000 1.064 ns/op 0 B/op 0 allocs/op
BenchmarkArithmetic/add-16 924924993 1.305 ns/op 0 B/op 0 allocs/op
BenchmarkJSON/small/encode-16 6004620 198.5 ns/op 160 B/op 3 allocs/op
BenchmarkJSON/small/decode-16 5047149 238.7 ns/op 152 B/op 2 allocs/op
BenchmarkJSON/large/encode-16 4739722 255.7 ns/op 176 B/op 3 allocs/op
BenchmarkJSON/large/decode-16 3737406 315.3 ns/op 152 B/op 2 allocs/op
BenchmarkBinary/small/encode-16 132380481 9.044 ns/op 16 B/op 1 allocs/op
BenchmarkBinary/small/decode-16 100000000 10.80 ns/op 16 B/op 1 allocs/op
BenchmarkBinary/large/encode-16 133549021 8.995 ns/op 16 B/op 1 allocs/op
BenchmarkBinary/large/decode-16 100000000 10.61 ns/op 16 B/op 1 allocs/op
PASS
ok github.com/nikolaydubina/fpmoney 15.804s
- 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 handlinggithub.com/Rhymond/go-money
: does not usefloat
orinterface{}
in parsing; currency is enumgithub.com/ferdypruis/iso4217
: skipped deprecated currencies to fit intouint8
and smaller struct size- https://en.wikipedia.org/wiki/ISO_4217