varunsrin / rusty_money

Money library for Rust
MIT License
93 stars 34 forks source link

Monetary amounts should not keep excess precision #71

Open HaKr opened 2 years ago

HaKr commented 2 years ago

When handling monetary amounts, the precision should be that of the minor unit instead of mathematical correct fractions. Current implementation seems to maintain the mathematical fraction

let amount: Money<Currency> = Money::from_major(100, iso::EUR) / 3;
assert_eq!(amount.to_string(), "€33,33");   // that is what I expect

let triple_amount: Money<Currency> = amount * 3;
assert_eq!(triple_amount.to_string(), "€99,99");    // assert fail because expected is "€100,00", but 3 x  €33,33 = €99,99
brokenthorn commented 1 year ago

But this is a rounding error, isn't it? It's present even in the most sophisticated accounting applications. The operations otherwise seems correct to me. Even the last one. 33,33 * 3 = 99,99 no matter the currency. This is happening because money has fixed precision and dividing 100 by 3 creates an approximate value using rounding to the money's minor units.

wischi-chr commented 2 months ago

Money doesn't really have a fixed precision, but the cash units have a fixed size.

Let's take gas prices for example, or prices of other products that are sold per weight. It's not uncommon to see 3 or more decimal places for the price per unit mass.

All the calculations are done with maximum precision and only at the last step is the price rounded to the nearest possible unit.

It would be pretty bad design if this library would always round to the smallest unit for all calculations, because it would introduce a lot of issues for the use-cases I mentioned.