Reputation: 935
I came across two methods to get precision in floating numbers - using round
or using the Decimal
package.
What i observed (with few examples tried in REPL) that both produce the same results:
>>> from decimal import Decimal
>>>
>>> 1/3
0.3333333333333333
>>>
>>> round(1/3)
0
>>> Decimal(1)/Decimal(3)
Decimal('0.3333333333333333333333333333')
>>>
>>> round(1/3, 2)
0.33
>>> (Decimal(1)/Decimal(3)).quantize(Decimal('0.01'))
Decimal('0.33')
>>>
This makes me think which method to use out of these two. Do both the methods always give same the same result for the same level of precision? Or am i missing out something here?
Upvotes: 3
Views: 1223
Reputation: 935
I think I was able to figure out few use cases where things might be different (I will update here as I find more differences):
One use case is division that results in recurring fractions and adding back, say, 10 / 3
:
using round
:
>>> r = round(10/3, 2)
>>> r
3.33
>>>
>>> sum([r, r, r])
>>> 9.99
>>>
using Decimal
(case 1):
>>> Q = Decimal("0.01")
>>>
>>> d1 = Decimal(10/3)
>>> d1
Decimal('3.333333333333333481363069950020872056484222412109375')
>>>
>>> sum([d1, d1, d1])
Decimal('10.00000000000000044408920985')
>>>
>>> sum([d1, d1, d1]).quantize(Q)
Decimal('10.00')
>>>
using Decimal
(case 2):
>>> Q = Decimal("0.01")
>>>
>>> d2 = Decimal(10) / Decimal(3)
>>> d2
Decimal('3.333333333333333333333333333')
>>>
>>> sum([d2, d2, d2]).quantize(Q)
Decimal('10.00')
>>>
Second use case is when you receive number with more decimal places than your precision value (something similar to what @H.Doebler mentioned in his answer here). Basically exploiting the round_half_to_even concept. Let's say you have set precision to 2, and get amounts like 0.125
and 0.145
and add them, the value should be 0.27
:
using round
:
>>> 0.125 + 0.145
0.27
>>>
>>> round(0.125, 2) + round(0.145, 2)
0.26
>>>
using Decimal
(case 1 - store quantized): (ERROR)
>>> Q = Decimal("0.01")
>>> d1 = Decimal(0.125).quantize(Q) + Decimal(0.145).quantize(Q)
>>> d1
Decimal('0.26')
>>> d1.quantize(Q)
Decimal('0.26')
>>>
using Decimal
(case 2 - store raw, quantize the result): (CORRECT)
>>> Q = Decimal("0.01")
>>> d2 = Decimal(0.125) + Decimal(0.145)
>>> d2
Decimal('0.2699999999999999900079927784')
>>> d2.quantize(Q)
Decimal('0.27')
>>>
I think round
and Decimal (case 1)
give wrong results because we are rounding (quantizing
in case of decimal
) the numbers before adding, but in case 2 we are quantizing after correctly representing the values in Decimal
. I guess it depends a lot on when you quantize..
Upvotes: 1
Reputation: 651
No, they do not always give the same result:
>>> (Decimal(645)/Decimal(1000)).quantize(Decimal("0.01"))
Decimal('0.64')
>>> round(645/1000, 2)
0.65
Builtin float
s rely on IEEE 754 double precision floating point representation with base 2, whereas decimal uses a base 10 representation. If you really want to round exactly to decimal places, float will not work, because most non-periodic non-trivial decimal number do not lie in the float
domain.
>>> f'{round(1/3, 2):.64f}'
'0.3300000000000000155431223447521915659308433532714843750000000000'
>>> f'{(Decimal(1)/Decimal(3)).quantize(Decimal("0.01")):.64f}'
'0.3300000000000000000000000000000000000000000000000000000000000000'
Upvotes: 2