Take me to the code, dammit. (I strongly recommend you look at the explanation. But then I would say that.)
Say you live in a country without 1, 5, or 10 cent coins (or the monetary equivalent), and you need to round a Decimal
number to 20 or 25 cents. Unfortunately, the quantize
method will only round your number to the argument’s exponent:
>>> from decimal import Decimal
>>> Decimal('0.3').quantize(Decimal('0.2'))
Decimal('0.3')
What we really want is the number rounded to the nearest multiple:
>>> round_up(Decimal('0.3'), Decimal('0.2'))
Decimal('0.4')
>>> round_down(Decimal('0.3'), Decimal('0.2'))
Decimal('0.2')
Luckily, this isn’t too difficult. But before I show the code, I want to walk through the operations we’ll perform. And don’t worry if maths isn’t your strong suit. In these examples, we’ll round 30 cents (0.3) to the nearest 20 cents (0.2), as indicated by the dotted line. First up, rounding down:
- Rescale the number continuum, so that 0.2 is equal to 1, and 0.3 becomes 1.5
- Round down to the nearest integer, so 1.5 becomes 1
- Rescale the number continuum, so that 1 is equal to 0.2, i.e. the original scale has been restored
Rounding up is pretty much the same:
- Rescale the number continuum, so that 0.2 is equal to 1, and 0.3 becomes 1.5
- Round up to the nearest integer, so 1.5 becomes 2
- Rescale the number continuum, so that 1 is equal to 0.2, i.e. the original scale has been restored
Finally, here’s the code. It even has unit tests, which probe the behaviour over various precision levels, exponents and values.