# money.convert
Converts a `Money` into another currency at a rate the caller supplies. It
does not look rates up: which rate applies (the day's ECB reference rate, the
rate on the invoice date, a contract rate) is the caller's decision, and this
takes it as an argument, so the same inputs always give the same answer.
**The rate is an exact fraction**, a `math.rational` `Rational`, in major
units, the way rates are quoted: GBP to EUR at 1.1734 is
`{ base: "GBP", quote: "EUR", rate: { numerator: 11734, denominator: 10000 } }`.
A decimal quote becomes a fraction by moving the point (`math.basis-points`
style), and the inverse rate is the same fraction upside down, exactly, so
converting back uses precisely the reciprocal rather than 1/1.1734 cut off
at some number of places. A fraction was chosen over "rate in micro-units"
because micro-units cannot hold a reciprocal and cap every rate at six
decimal places.
**Minor units are handled for you.** The rate is in major units, so the
amount is scaled by each currency's ISO 4217 decimal places (from
`money.currency-digits`): 10.00 GBP at 190.12 is 1,901 JPY, not 190,120.
**One rounding**, at the end, to the quote currency's minor unit, with the
`math.round-div` mode you pass. Everything before it is exact. 1.00 EUR at
1.005 is exactly 1.005 USD; `half-up` gives 1.01, where a float computes
100 × 1.005 as 100.49999999999999 and rounds it to 1.00.
**The rate carries its currencies.** Applying a GBP-to-EUR rate to a USD
amount is an error, not a conversion. To go the other way, swap `base` and
`quote` and turn the fraction over; this never inverts a rate on its own.
Edges: the rate must be positive; zero converts to zero; negative amounts
(refunds) convert symmetrically under `half-up` and `half-even`. Intermediate
values follow `math.rational`'s limits (numerator and denominator within
2^53 - 1 after reducing); an amount and rate so large that the exact product
does not fit is refused with "rational overflow" rather than rounded.