Problems with Rounding Decimals (python)

时光怂恿深爱的人放手 提交于 2019-12-02 22:15:13

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

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')

Using round() for any purpose has difficulties:

(1) In Python 2.7, round(Decimal('39.142')) produces 39.14 i.e. a float; 3.2 produces a Decimal.

>>> import decimal; round(decimal.Decimal('2.000000000000000000001'),50)
2.0   <<<=== ***float***

(2) round() has only one rounding mode; Decimal.quantize has many.

(3) For all input types, the single rounding mode changed: Python 2.7 rounds away from 0 (expected in most business applications) whereas Python 3.2 rounds to the nearest even multiple of 10**-n (less expected in business).

Use Decimal.quantize()

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!

As stated above, round() returns a float if provided with a Decimal. For exact calculations, I suggest to do them with not rounded down Decimals, and for rounding the final result, use a function:

    def dround(decimal_number, decimal_places):
        return decimal_number.quantize(Decimal(10) ** -decimal_places)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!