How to round a Python Decimal to 2 decimal places?
Asked Answered
F

8

16

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!

Felon answered 10/5, 2014 at 16:3 Comment(5)
Wouldn't it make more sense to use integers (cents instead of dollars)? That would also be faster.Carob
To set the precision of a decimal number: decimal.getcontext().prec=2.Interstate
@ooga: That deals with the number of significant digits, not the number of decimal digits and is therefore unsuitable (it turns 12345678 into 1.2E+7, but only after doing calculations on it, not right after definition).Carob
@TimPietzcker I see. In that case I agree that an integer is best.Interstate
It's worth noting that round does return a Decimal instance directly with Python 3; it's only on Python 2 that it converts to float. Even then, though, you'll still need quantize if you want anything other than the default ROUND_HALF_EVEN rounding mode.Boise
M
23

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.

Mannikin answered 1/3, 2022 at 17:5 Comment(2)
round(Decimal(0.5), 0) returns 0 ?Cardioid
round uses ROUND_HALF_EVEN, where even numbers (including 0) round down, and odd numbers round up. This helps avoid a slight skewing of results when dealing with large data sets.Abukir
C
19

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().

Carob answered 10/5, 2014 at 16:12 Comment(2)
Note that in more recent versions of Python you can use the built in round(), since rounding now returns a Decimal if one of provided.Felon
@kramer65: True, but it still only uses the ROUND_HALF_EVEN method.Carob
R
8

The first one you tried worked just fine for me.

import decimal

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

<class 'decimal.Decimal'>

Runnel answered 15/7, 2015 at 8:21 Comment(3)
round will make the result an object of float classUnspeakable
I just tested it in Python version 3.8 and I see that you're right. I guess that was changed in a version of Python released after I tested this.Felon
sys.version_info(major=3, minor=8, micro=5, releaselevel='final', serial=0) gives <class 'decimal.Decimal'> for print(type(round(decimal.Decimal(1.1233999999999999541699935434735380113124847412109375), 2)))Diminutive
L
1

This has came to my mind:

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

but I don't know how fast it is.

Lailalain answered 10/5, 2014 at 16:12 Comment(0)
L
1

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
Longheaded answered 1/8, 2016 at 10:1 Comment(2)
In Jupyter Notebook, %precision appears to only set pretty-print precision for float values, not Decimal values.Stroke
Also, getcontext().prec = 2 will cause Decimal operations to retain 2 significant digits, not 2 significant decimal places. E.g. Decimal('1') * Decimal('123') gives Decimal('1.2E+2').Stroke
P
0

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)
Puny answered 14/10, 2021 at 21:31 Comment(1)
Sorry, you have to do: import decimal from decimal import Decimal. I know it's not great to have one Decimal() and another decimal.Decimal but I'm not sure if I can edit my answer.Puny
U
0

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

Unswear answered 23/11, 2021 at 4:23 Comment(0)
F
0

@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)
Fasciculus answered 11/3, 2022 at 19:16 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewHolmic

© 2022 - 2024 — McMap. All rights reserved.