Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Proper' rounding in Python, to 3 decimal places

I may be missing something essential, but I can't figure out a way to 'properly' round floats/Decimals in Python (2.7), at least to three decimal places. By 'properly' I mean that 1.2225 should round to 1.223, and 1.2224 should round to 1.222.

I know round won't work for floats in Python, by design, but I can't seem to get Decimal to behave as expected, nor the ceil function. Looking for built-in functionality rather than custom function workarounds preferably, but open to both.

>>> x = 1.2225                                   # expected: 1.223
>>> round(x, 3)               
1.222                                            # incorrect

>>> from math import ceil

>>> ceil(x * 1000.0) / 1000.0
1.223                                            # correct
>>> y = 1.2224                                   # expected: 1.222
>>> ceil(y * 1000.0) / 1000.0 
1.223                                            # incorrect

>>> from decimal import Decimal, ROUND_UP, ROUND_HALF_UP

>>> x = Decimal(1.2225)
>>> x.quantize(Decimal('0.001'), ROUND_UP)
Decimal('1.223')                                 # correct
>>> y = Decimal(1.2224)
>>> y.quantize(Decimal('0.001'), ROUND_UP)
Decimal('1.223')                                 # incorrect

>>> y.quantize(Decimal('0.001'), ROUND_HALF_UP)
Decimal('1.222')                                 # correct
>>> x.quantize(Decimal('0.001'), ROUND_HALF_UP)
Decimal('1.222')                                 # incorrect

Is there a way to get the desired result?

like image 695
user2524282 Avatar asked Dec 07 '16 15:12

user2524282


2 Answers

The problem is that Decimal(1.2225) is not what you expect it to be:

>>> Decimal(1.2225)
Decimal('1.2224999999999999200639422269887290894985198974609375')

You are using a float to the create the decimal, but that float is already too imprecise for your use case. As you can see, it’s actually a 1.222499 so it is smaller than 1.2225 and as such would correctly round down.

In order to fix that, you need to create decimals with correct precision, by passing them as strings. Then everything works as expected:

>>> x = Decimal('1.2225')
>>> x.quantize(Decimal('0.001'), ROUND_HALF_UP)
Decimal('1.223')
>>> y = Decimal('1.2224')
>>> y.quantize(Decimal('0.001'), ROUND_HALF_UP)
Decimal('1.222')
like image 200
poke Avatar answered Sep 17 '22 03:09

poke


Here are three solution in this link, I hope this would help you exactly what you want to do. https://gist.github.com/jackiekazil/6201722

from decimal import Decimal

# First we take a float and convert it to a decimal
x = Decimal(16.0/7)

# Then we round it to 2 places
output = round(x,2)
# Output to screen
print output
like image 38
Ahsan Mahmood Avatar answered Sep 20 '22 03:09

Ahsan Mahmood