A monetary amount pairs a decimal quantity with a currency. In a currency converter, that pair is the input to Convert and the form of its result: 34.98 USD can become an amount in EUR. Keeping Amount in a reusable money package separates this domain logic from the command-line interface. section 6.1 section 6.1.1
Represent the quantity with fixed precision rather than defaulting to floating-point arithmetic. Decimal stores an int64 count of subunits and a precision: 1.52 is stored as 152 with precision 2. ParseDecimal accepts a string, avoiding floating-point imprecision at input. Amount holds that Decimal alongside a Currency; its fields are unexported so callers cannot change a constructed value directly. section 6.2.2 section 6.2.4 section 6.1.1
Reject incompatible precision
Canadian dollars have cent precision. A quantity of 19.8875 has four decimal places, so NewAmount must return ErrTooPrecise rather than create that amount. When the quantity fits, the constructor sets its precision to the currency’s precision. section 6.2.6
Listing 6.15 amount.go: NewAmount function
const (
// ErrTooPrecise is returned if the number is
// too precise for the currency.
ErrTooPrecise = Error("quantity is too precise")
)
// NewAmount returns an Amount of money.
func NewAmount(quantity Decimal, currency Currency) (Amount, error) {
if quantity.precision > currency.precision { #1
// In order to avoid converting 0.00001 cent, let's exit now.
return Amount{}, ErrTooPrecise
}
quantity.precision = currency.precision #2
return Amount{quantity: quantity, currency: currency}, nil
}
The constructor checks currency precision before returning an Amount. section 6.2.6
Keep exchange-rate application outside Amount: an amount describes money, while conversion needs a rate and a target currency. Applying the rate creates a new amount without changing the input. The result uses the target currency’s precision; the shown implementation discards excess fractional subunits or adds subunits when more precision is needed. Convert then validates the result against its supported size and precision limits. section 6.3.1 section 6.3.2
Test the boundaries as well as ordinary conversions: reject an overly precise amount, check outputs at different target precisions, and include large values and very small rates. Test helpers should stop on parsing or construction errors instead of silently using zero values. Fixed precision avoids some floating-point problems, but the implementation still imposes a bounded range of supported values. section 6.2.6 section 6.3.1 section 6.3.2

