Rhymond/go-money

NewFromFloat shifts the value of the amount in certain currencies.

kotaroyamazaki opened this issue · 4 comments

e,g)
Display amount of SDG

input 136.98
expected: $136.98
actual: $136.97

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

I am having the same issue:

origPrice := float64(18.99)
cents := money.NewFromFloat(origPrice, money.USD)
int(cents.Amount()) => this is 1898

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.

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)

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?