Rhymond / go-money

Go implementation of Fowler's Money pattern
MIT License
1.65k stars 147 forks source link

NewFromFloat shifts the value of the amount in certain currencies. #124

Open kotaroyamazaki opened 1 year ago

kotaroyamazaki commented 1 year ago

e,g) Display amount of SDG

input 136.98 expected: $136.98 actual: $136.97

seeing is believing ↓ https://go.dev/play/p/l4imxLkT70u

kylebragger commented 1 year ago

I am having the same issue:

origPrice := float64(18.99)
cents := money.NewFromFloat(origPrice, money.USD)
int(cents.Amount()) => this is 1898
SkipHendriks commented 1 year ago

Floats are the issue:

@kotaroyamazaki https://go.dev/play/p/oNzxYVoZK4K

@kylebragger https://go.dev/play/p/j9lwBeNUllb

The idea of this package is to use the smallest unit of the currency, so cent in case of dollars and use that as int. So 136.98 becomes 13698.

This is know as Fowler's Money pattern which is what this package implements. It is designed to overcome floating point issues like the one you described.

egtann commented 8 months ago

I was starting with a string in the form of a float, so my solution was to strip non-numeric characters with regex, parse the remaining string as an int, and pass that int to the money package, like this:

nonNumeric := regexp.MustCompile(`\D*`)
amountStr := "18.99"
numStr := nonNumeric.ReplaceAllString(amountStr, "")
num, _ := strconv.ParseInt(numStr, 10, 32) // handle err
val := money.New(num, currency)
vaihtovirta commented 7 months ago

We encountered the same issue, and decided to switch to https://github.com/shopspring/decimal.

    amount := 73708.43

    // money
    moneyAmount := money.NewFromFloat(amount, "EUR")
    fmt.Println("money cents", moneyAmount.Amount()) // decimal 7370843

    // decimal
    decimalAccount := decimal.NewFromFloat(amount)
    decimalSubunits := decimal.New(100, 0)
    decimalAmountCents := decimalAccount.Mul(decimalSubunits)
    fmt.Println("decimal cents", decimalAmountCents.IntPart()) // money 7370842

@SkipHendriks @Rhymond Hey, just a thought, if floats are causing trouble, why was the NewFromFloat method included in the library to begin with? Maybe it's worth mentioning in the README so other people don't stumble over this again?