I've got a system that handles account & COD transactions. When a client is paying via COD, I need to round up or round down the cents.
Eg.
Total = 64.42 - rounds to: 64.40
Total = 64.57 - rounds to 64.60
I'm trying to figure out the best way to do this in python and also store the difference in a decimal field in a database.
I'm using DJANGO. I was trying to use the ROUND_05UP part of decimal but I'm not sure how to do it. I wrote a custom filter to display it, but that's not even working:
@register.filter
def currency(value, arg='en_US', symbol=True):
saved = '.'.join([x for x in locale.getlocale() if x]) or (None, None)
given = arg and ('.' in arg and str(arg) or str(arg) + '.UTF-8')
# Workaround for Python bug 1699853 and other possibly related bugs.
if '.' in saved and saved.split('.')[1].lower() in ('utf', 'utf8'):
saved = saved.split('.')[0] + '.UTF-8'
if saved == (None, None) and given == '':
given = 'en_US.UTF-8'
try:
locale.setlocale(locale.LC_ALL, given)
return locale.currency(value or 0, symbol, True)
except (TypeError, locale.Error):
return ''
finally:
locale.setlocale(locale.LC_ALL, saved)
@register.filter
def currency_cash(value):
value = decimal.Decimal(value)
return currency(value.quantize(Decimal(10) ** -2, rounding=decimal.ROUND_05UP))
Does anyone have a function written that does this?
UPDATE: The reason for this in australia we have no coins less then 5 cents. So 45.55 would stay as 45.55.
Cheers, Ben
This type of rounding you are talking about is called Swedish rounding:
and so on.
from decimal import Decimal as D, ROUND_HALF_EVEN # or ROUND_HALF_UP
def round_to_5_cents(d):
"""
Round a Decimal value to the nearest multiple of 0.05,
"""
return (d*2).quantize(D('0.1'), ROUND_HALF_EVEN)/D(2)
for x in range(80, 120):
d = D(x) * D('0.01')
print "{0} rounds to {1}".format(d, round_to_5_cents(d))
Sample output:
0.80 rounds to 0.8
0.81 rounds to 0.8
0.82 rounds to 0.8
0.83 rounds to 0.85
0.84 rounds to 0.85
0.85 rounds to 0.85
0.86 rounds to 0.85
0.87 rounds to 0.85
0.88 rounds to 0.9
0.89 rounds to 0.9
0.90 rounds to 0.9
0.91 rounds to 0.9
0.92 rounds to 0.9
0.93 rounds to 0.95
0.94 rounds to 0.95
0.95 rounds to 0.95
0.96 rounds to 0.95
0.97 rounds to 0.95
0.98 rounds to 1.0
0.99 rounds to 1.0
1.00 rounds to 1.0
1.01 rounds to 1.0
1.02 rounds to 1.0
1.03 rounds to 1.05
1.04 rounds to 1.05
1.05 rounds to 1.05
1.06 rounds to 1.05
1.07 rounds to 1.05
1.08 rounds to 1.1
1.09 rounds to 1.1
1.10 rounds to 1.1
1.11 rounds to 1.1
1.12 rounds to 1.1
1.13 rounds to 1.15
1.14 rounds to 1.15
1.15 rounds to 1.15
1.16 rounds to 1.15
1.17 rounds to 1.15
1.18 rounds to 1.2
1.19 rounds to 1.2
@Mark Dickingson's answer does a different thing. It leads to larger accumulated rounding error.
If I understand correctly, the incoming amount is already an exact integer number of cents, and you just want to round to the nearest multiple of 10 cents, but leaving values that already end in 5 as they are. Is that correct? Here's a simple solution using the remainder_near method.
from decimal import Decimal
def round_to_10_cents(x):
"""
Round a Decimal value to the nearest multiple of 0.10,
unless already an exact multiple of 0.05.
"""
remainder = x.remainder_near(Decimal('0.10'))
if abs(remainder) == Decimal('0.05'):
return x
else:
return x - remainder
# Test code.
for x in range(80, 120):
y = Decimal(x) / Decimal('1E2')
print "{0} rounds to {1}".format(y, round_to_10_cents(y))
Sample output:
0.80 rounds to 0.80
0.81 rounds to 0.80
0.82 rounds to 0.80
0.83 rounds to 0.80
0.84 rounds to 0.80
0.85 rounds to 0.85
0.86 rounds to 0.90
0.87 rounds to 0.90
0.88 rounds to 0.90
0.89 rounds to 0.90
0.90 rounds to 0.90
0.91 rounds to 0.90
0.92 rounds to 0.90
0.93 rounds to 0.90
0.94 rounds to 0.90
0.95 rounds to 0.95
0.96 rounds to 1.00
0.97 rounds to 1.00
0.98 rounds to 1.00
0.99 rounds to 1.00
1.00 rounds to 1.00
1.01 rounds to 1.00
1.02 rounds to 1.00
1.03 rounds to 1.00
1.04 rounds to 1.00
1.05 rounds to 1.05
1.06 rounds to 1.10
1.07 rounds to 1.10
1.08 rounds to 1.10
1.09 rounds to 1.10
1.10 rounds to 1.10
1.11 rounds to 1.10
1.12 rounds to 1.10
1.13 rounds to 1.10
1.14 rounds to 1.10
1.15 rounds to 1.15
1.16 rounds to 1.20
1.17 rounds to 1.20
1.18 rounds to 1.20
1.19 rounds to 1.20
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With