Doing money calculation and perform rounding at the end
See original GitHub issueBy default the Money instance will round every math operation (+. -, *. /, etc) that might have fractions of cents using a RoundingMode.
This will cause issues with multiple operations and money loss. If you multiple by a couple of numbers, do some division, add a few things, do some more division, you’ll end up losing multiple cents along the way. This happens frequently in financial applications and many others as well.
There needs to be a way to perform multiple calculations on a Money instance without any rounding and then round at the end. Perhaps a MoneyCalculater (or building pattern) of some sort that can provide multiple operations without rounding and in the end provide back a Money instance that is rounded.
The best is probably to use a delegate like Func<Money, Money>
, Action<Money>
or a custom delegate.
See an example like https://github.com/gigs2go/joda-money-calculator/tree/master/joda-money-calculator or the builder pattern in JavaMoney.
Issue Analytics
- State:
- Created 8 years ago
- Reactions:3
- Comments:15 (5 by maintainers)
Top GitHub Comments
It’s not uncommon for prices to be defined in fractions of cents. I think it would be great for this library to support that, and make rounding amounts opt-in. For my use-case I need greater precision on the line items, and also be able to serialise with great precision. So having this library take care of calculations and returning a rounded value wouldn’t work for me. I would simply want to opt-out of rounding.
Take for example an Azure VM (B1S), which is currently priced at €0.0034/hour for my region. I wouldn’t be able to use this library to perform any calculations based on that rate. Another example would be cash payments in the Netherlands: amounts are rounded to the nearest €0.05. When you have to pay €9.99 with a €10 note, you get no change. However I wouldn’t want to use a software library that only allows me to use multiples of €0.05; I’d still need the €0.01 granularity on the line items.
Rounding money isn’t just a matter of presentation and doing this in a view or when converting to a different type. Whether money value is rounded is important domain logic, and is intrinsic to the Money type.
Money is based on a real agreed system and can’t represent values out of its range. For Euro’s to lowest positive value is a cent, €0.01, and you can’t split this. That is what this type does for you, it protects you from passing unrounded money, which isn’t valid. Your domain model should always return or store rounded values for Money.
An invoice is a good example where using the unrounded value underneath for the calculation, but presenting it rounded, would get you in trouble (you are missing a cent).
Compare this with for example the Int type. When I do the following calculation
7 / 3
, the result would be an int with the value 2. An int type can’t represent a floating point so it’s rounded. You wouldn’t expect that the result of the division is hidden somewhere in the Int type and would effect the next calculation. If you want result as a floating point than you should use a floating point type.So rounding should be the default, but of course there are exceptions where you want to do calculations and only do the rounding in the end or in certain in-between-steps. You are basically want to convert Money to a decimal type, do calculations and convert it back to Money again. The solution should be explicit that you want to work with unrounded values.