Question

I've got a python Decimal (a currency amount) which I want to round to two decimal places. I tried doing this using the regular round() function. Unfortunately, this returns a float, which makes it unreliable to continue with:

>>> from decimal import Decimal
>>> a = Decimal('1.23456789')
>>> type(round(a, 2))
<type 'float'>

in the decimal module, I see a couple things in relation to rounding:

  • ROUND_05UP
  • ROUND_CEILING
  • ROUND_DOWN
  • ROUND_FLOOR
  • ROUND_HALF_DOWN
  • ROUND_HALF_EVEN
  • ROUND_HALF_UP
  • ROUND_UP
  • Rounded

I think that none of these actually give what I want though (or am I wrong here?).

So my question: does anybody know how I can reliably round a Python Decimal to 2 decimal places so that I have a Decimal to continue with? All tips are welcome!

Was it helpful?

Solution 2

Since Python 3.3 you can use round() with a Decimal and it will return you a Decimal:

>>> from decimal import Decimal
>>> round(Decimal('3.14159265359'), 3)
Decimal('3.142')

See details in this answer.

OTHER TIPS

You could use the quantize() method:

>>> import decimal
>>> decimal.getcontext().prec = 20
>>> a = decimal.Decimal(321.12345)
>>> a
Decimal('321.12344999999999117790139280259609222412109375')
>>> TWO_PLACES = decimal.Decimal("0.01")
>>> a.quantize(TWO_PLACES)
Decimal('321.12')

The Python docs have a nice recipe how to build a string representation of currency values using Decimals and .quantize().

The first one you tried worked just fine for me.

import decimal

type(round(decimal.Decimal('1.23456789'),2))

<class 'decimal.Decimal'>

If you are using Jupyter Notebook, just add the magic function %precision 2 for 2 decimal and so forth.

But if your values are not float() type then you might use the following:

 from decimal import getcontext, Decimal
 getcontext().prec = 2
 # for example, take integer value
 mynum = 2
 type(mynum) # Int
 print round(Decimal(mynum), 2) # 10.0

This has came to my mind:

import decimal
decimal.Decimal(str(round(a, 2)))

but I don't know how fast it is.

Not sure why they don't have a built-in function, but I made one. I know it's not technically "trueRound" but you can change that if you want. Tested on python 3.9

    def trueRound(num: float or str, decimalPlaces: int) -> float:
        a = Decimal(str(num))
        places_str = "0."
        for i in range(decimalPlaces - 1):
            places_str += "0"
        places_str += "1"
        PLACES = decimal.Decimal(places_str)
        result = a.quantize(PLACES)
        return float(result)

More Pythonic code based on the answer of @countinglambdastosleep

from decimal import Decimal, ROUND_HALF_EVEN

def decimalize(n, prec=8):
    n = n if isinstance(n, Decimal) else Decimal(n)
    fmt = ".{}1".format("0" * (prec - 1))
    return n.quantize(Decimal(fmt), rounding=ROUND_HALF_EVEN)

Why ROUND_HALF_EVEN? You may find answers here

@Do Anh Tu, nice answer...just one minor change so as to handle float inputs as well, apart from Decimal and string inputs, can change to Decimal(n) to Decimal(str(n)), following being the overall code:

from decimal import Decimal, ROUND_HALF_EVEN

def decimalize(n, prec=8):
    n = n if isinstance(n, Decimal) else Decimal(str(n))
    fmt = ".{}1".format("0" * (prec - 1))
    return n.quantize(Decimal(fmt), rounding=ROUND_HALF_EVEN)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top