Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with Rounding Decimals (python)

In my program, decimal accuracy is very important.
A lot of my calculations must be accurate to many decimal places (such as 50).

Because I am using python, I have been using the decimal module (with context().prec = 99. ie; Set to have 99 decimal places of accuracy when instantiating a decimal object)
as pythonic floats don't allow anywhere near such accuracy.

Since I wish for the User to specify the decimal places of accuracy of the calculations, I've had to implement several round() functions in my code.
Unfortuneately, the inbuilt round function and the decimal object do not interact well.

round(decimal.Decimal('2.000000000000000000001'),50)

# Number is 1e-21.   There are 21 decimal places, much less than 50.

Yet the result is 2.0 instead of 2.000000000000000000001
The round function is not rounding to 50. Much less!

Yes, I have made sure that the over-rounding does not occur on instantiation of the Decimal object, but after calling round.
I always pass strings representing floats to the Decimal constructor, never pythonic floats.

Why is the round function doing this to me?
(I realise that it was probably originally designed for pythonic floats which can never have so many decimal places, but the documentation claims that the Decimal object integrates perfectly into python code and is compatible with the inbuilt python functions!)

Thanks profusely!
(This has me quite unnerved, since this problem undermines the use of the entire program)

Specs:
python 2.7.1
Windows 7
decimal module (inbuilt)

like image 554
Anti Earth Avatar asked Jan 15 '12 10:01

Anti Earth


People also ask

How do I fix rounding in Python?

To implement the “rounding down” strategy in Python, we can follow the same algorithm we used for both trunctate() and round_up() . First shift the decimal point, then round to an integer, and finally shift the decimal point back.

Why round function is not working in Python?

Surprisingly, that is not how rounding works in Python. Rounding half numbers does not round up, and in fact, it doesn't always round down either. Instead, it rounds to the nearest even number. It is worth pointing out that besides the half-number case, round() works as expected, in that it returns the nearest integer.

How do you round up decimals in Python?

Python Programming offers a built-in round() function which rounds off a number to the given number of digits and makes rounding of numbers easier. The function round() accepts two numeric arguments, n and n digits and then returns the number n after rounding it to ndigits.

Why is .5 rounded down Python?

5 rounded down and not up if int is called, how come this is the case? This comes from the Median work where the first print produces, say a value of 5 then after being divided by 2 and calling int it becomes 2.


3 Answers

Since round() coerces its input to a regular binary float, the preferred way to round decimal objects is with the quantize() method:

>>> from decimal import Decimal
>>> d = Decimal('2.000000000000000000001')

>>> d.quantize(Decimal(10) ** -20)     # Round to twenty decimal places
Decimal('2.00000000000000000000')

To chop-off the trailing zeros, apply normalize() to the rounded result:

>>> d = Decimal('2.000000000000000000001')
>>> d.quantize(Decimal(10) ** -20).normalize()
Decimal('2')
like image 131
Raymond Hettinger Avatar answered Oct 19 '22 05:10

Raymond Hettinger


The problem, as near as I can determine it, is that round() is returning a Python float type, and not a Decimal type. Thus it doesn't matter what precision you set on the decimal module, because once you call round() you no longer have a Decimal.

To work around this, you'll probably have to come up with an alternative way to round your numbers that doesn't rely on round(). Such as Raymond's suggestion.

You may find this short ideone example illustrative: http://ideone.com/xgPL9

like image 41
aroth Avatar answered Oct 19 '22 06:10

aroth


Try the following...

from decimal import Decimal, ROUND_HALF_UP, getcontext

getcontext().prec = 51

def round_as_decimal(num, decimal_places=2):
    """Round a number to a given precision and return as a Decimal

    Arguments:
    :param num: number
    :type num: int, float, decimal, or str
    :returns: Rounded Decimal
    :rtype: decimal.Decimal
    """
    precision = '1.{places}'.format(places='0' * decimal_places)
    return Decimal(str(num)).quantize(Decimal(precision), rounding=ROUND_HALF_UP)

round_as_decimal('2.000000000000000000001', decimal_places=50)

Hope that helps!

like image 5
kamalgill Avatar answered Oct 19 '22 04:10

kamalgill