67

How can I round up a number to the second decimal place in python? For example:

0.022499999999999999

Should round up to 0.03

0.1111111111111000

Should round up to 0.12

If there is any value in the third decimal place, I want it to always round up leaving me 2 values behind the decimal point.

Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
user1202589
  • 683
  • 1
  • 5
  • 4
  • 5
    I'd suggest to read [Floating Point Arithmetic: Issues and Limitations](http://docs.python.org/tutorial/floatingpoint.html) from the Python tutorial before you go on. – Sven Marnach Feb 10 '12 at 17:44
  • Also, consider whether you really want to round the values, or you just want to *display* them with 3 decimal places... – Wooble Feb 10 '12 at 19:27
  • https://stackoverflow.com/a/63035788/3907629 – NMC Jul 22 '20 at 14:02

13 Answers13

55

Python includes the round() function which lets you specify the number of digits you want. From the documentation:

round(x[, n])

Return the floating point value x rounded to n digits after the decimal point. If n is omitted, it defaults to zero. The result is a floating point number. Values are rounded to the closest multiple of 10 to the power minus n; if two multiples are equally close, rounding is done away from 0 (so. for example, round(0.5) is 1.0 and round(-0.5) is -1.0).

So you would want to use round(x, 2) to do normal rounding. To ensure that the number is always rounded up you would need to use the ceil(x) function. Similarly, to round down use floor(x).

Community
  • 1
  • 1
simchona
  • 1,878
  • 3
  • 19
  • 18
  • 23
    Good suggestion, but it doesn't round **up** as the OP seems to require. – NPE Feb 10 '12 at 17:45
  • 1
    "Round up" isn't the same as normal rounding. Look at the examples in the question. – Mark Ransom Feb 10 '12 at 17:46
  • @Mark Because adding in the exact code to do `ceil` would mean copying your answer, I'm leaving mine as (almost) is and upvoting yours. – simchona Feb 10 '12 at 17:58
38
from math import ceil

num = 0.1111111111000
num = ceil(num * 100) / 100.0

See:
math.ceil documentation
round documentation - You'll probably want to check this out anyway for future reference

Edwin
  • 2,074
  • 1
  • 21
  • 40
  • You don't need `round()` here -- it won't change the result in any way. – Sven Marnach Feb 10 '12 at 17:49
  • 11
    Gives wrong results in following cases: `math.ceil((1.11 * 100.0)) / 100.0` comes out to be `1.12` Because: `1.11 * 100.0` has value `111.00000000000001` – nimeshkiranverma Feb 23 '17 at 15:59
  • 2
    @nimeshkiranverma see [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) Use the [`decimal` module](https://docs.python.org/3/library/decimal.html) if you need exact decimal results. – Mark Ransom Nov 16 '17 at 14:16
  • 1
    Based on the observation made by @nimeshkiranverma, you do need `round()` to get the desired result: `math.ceil(round(1.11 * 100.0)) / 100.0` – Sean Bearden Dec 09 '20 at 04:32
27
x = math.ceil(x * 100.0) / 100.0
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 3
    I had to stare at this for a while before I realized this is even more pythonic than my solution. – Edwin Feb 10 '12 at 17:49
22

Updated answer:
The problem with my original answer, as pointed out in the comments by @jpm, is the behavior at the boundaries. Python 3 makes this even more difficult since it uses "bankers" rounding instead of "old school" rounding. However, in looking into this issue I discovered an even better solution using the decimal library.

import decimal

def round_up(x, place=0):
    context = decimal.getcontext()
    # get the original setting so we can put it back when we're done
    original_rounding = context.rounding
    # change context to act like ceil()
    context.rounding = decimal.ROUND_CEILING

    rounded = round(decimal.Decimal(str(x)), place)
    context.rounding = original_rounding
    return float(rounded)

Or if you really just want a one-liner:

import decimal
decimal.getcontext().rounding = decimal.ROUND_CEILING

# here's the one-liner
float(round(decimal.Decimal(str(0.1111)), ndigits=2))
>> 0.12

# Note: this only affects the rounding of `Decimal`
round(0.1111, ndigits=2)
>> 0.11

Here are some examples:

round_up(0.022499999999999999, 2)
>> 0.03
round_up(0.1111111111111000, 2)
>> 0.12
round_up(0.1111111111111000, 3)
>> 0.112

round_up(3.4)
>> 4.0

# @jpm - boundaries do what we want
round_up(0.1, 2)
>> 0.1
round_up(1.1, 2)
>> 1.1

# Note: this still rounds toward `inf`, not "away from zero"
round_up(2.049, 2)
>> 2.05
round_up(-2.0449, 2)
>> -2.04

We can use it to round to the left of the decimal as well:

round_up(11, -1)
>> 20

We don't multiply by 10, thereby avoiding the overflow mentioned in this answer.

round_up(1.01e308, -307)
>> 1.1e+308

Original Answer (Not recommended):
This depends on the behavior you want when considering positive and negative numbers, but if you want something that always rounds to a larger value (e.g. 2.0449 -> 2.05, -2.0449 -> -2.04) then you can do:

round(x + 0.005, 2)

or a little fancier:

def round_up(x, place):
    return round(x + 5 * 10**(-1 * (place + 1)), place)

This also seems to work as follows:

round(144, -1)
# 140
round_up(144, -1)
# 150
round_up(1e308, -307)
# 1.1e308
ajp619
  • 670
  • 7
  • 11
  • 1
    This is not guaranteed to work... An example: `round(0.1 + 0.005, 2) == 0.11` whereas `round(1.1 + 0.005, 2) == 1.10` – jpm Apr 16 '20 at 04:43
  • @jpm: Good point. I looked into it and it turns out the boundaries are hard to deal with correctly using round, especially now that round uses "bankers" rounding instead of "old school rounding". I've updated my answer with a better solution. – ajp619 Apr 17 '20 at 15:10
  • This answer could be improved by specifying which rounding you mean with "oldschool": https://en.wikipedia.org/wiki/Rounding and removing the quotation marks around bankers rounding – Hakaishin Jan 12 '23 at 09:29
15

Extrapolating from Edwin's answer:

from math import ceil, floor
def float_round(num, places = 0, direction = floor):
    return direction(num * (10**places)) / float(10**places)

To use:

>>> float_round(0.21111, 3, ceil)  #round up
>>> 0.212
>>> float_round(0.21111, 3)        #round down
>>> 0.211
>>> float_round(0.21111, 3, round) #round naturally
>>> 0.211
Patrick Perini
  • 22,555
  • 12
  • 59
  • 88
  • 8
    Gives wrong results in following cases: `math.ceil((1.11 * 100.0)) / 100.0` comes out to be `1.12` Because: `1.11 * 100.0` has value `111.00000000000001` – nimeshkiranverma Feb 23 '17 at 15:59
5

Note that the ceil(num * 100) / 100 trick will crash on some degenerate inputs, like 1e308. This may not come up often but I can tell you it just cost me a couple of days. To avoid this, "it would be nice if" ceil() and floor() took a decimal places argument, like round() does... Meanwhile, anyone know a clean alternative that won't crash on inputs like this? I had some hopes for the decimal package but it seems to die too:

>>> from math import ceil
>>> from decimal import Decimal, ROUND_DOWN, ROUND_UP
>>> num = 0.1111111111000
>>> ceil(num * 100) / 100
0.12
>>> float(Decimal(num).quantize(Decimal('.01'), rounding=ROUND_UP))
0.12
>>> num = 1e308
>>> ceil(num * 100) / 100
Traceback (most recent call last):
  File "<string>", line 301, in runcode
  File "<interactive input>", line 1, in <module>
OverflowError: cannot convert float infinity to integer
>>> float(Decimal(num).quantize(Decimal('.01'), rounding=ROUND_UP))
Traceback (most recent call last):
  File "<string>", line 301, in runcode
  File "<interactive input>", line 1, in <module>
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]

Of course one might say that crashing is the only sane behavior on such inputs, but I would argue that it's not the rounding but the multiplication that's causing the problem (that's why, eg, 1e306 doesn't crash), and a cleaner implementation of the round-up-nth-place fn would avoid the multiplication hack.

Jacob Eliosoff
  • 119
  • 1
  • 2
  • The problem you're running into with this use of `Decimal` is that the result of the quantize expression needs 311 digits to express, and the current precision is too small. If you do a `from decimal import getcontext; getcontext().prec = 400` beforehand, this will "work", for some value of "work". Or you could note that any float larger than 2**52 in absolute value must already be an integer, so the rounding will have no effect. And yes, I agree it would nice if "ceil" and "floor" took a decimal places argument. – Mark Dickinson Aug 30 '15 at 16:56
5

The python round function could be rounding the way not you expected.

You can be more specific about the rounding method by using Decimal.quantize

eg.

from decimal import Decimal, ROUND_HALF_UP
res = Decimal('0.25').quantize(Decimal('0.0'), rounding=ROUND_HALF_UP)
print(res) 
# prints 0.3

More reference:

https://gist.github.com/jackiekazil/6201722

James Lin
  • 25,028
  • 36
  • 133
  • 233
3

Here is a more general one-liner that works for any digits:

import math
def ceil(number, digits) -> float: return math.ceil((10.0 ** digits) * number) / (10.0 ** digits)

Example usage:

>>> ceil(1.111111, 2)
1.12

Caveat: as stated by nimeshkiranverma:

>>> ceil(1.11, 2) 
1.12  #Because: 1.11 * 100.0 has value 111.00000000000001
Ferro
  • 1,863
  • 2
  • 14
  • 20
2
def round_up(number, ndigits=None):
    # start by just rounding the number, as sometimes this rounds it up
    result = round(number, ndigits if ndigits else 0)
    if result < number:
        # whoops, the number was rounded down instead, so correct for that
        if ndigits:
            # use the type of number provided, e.g. float, decimal, fraction
            Numerical = type(number)
            # add the digit 1 in the correct decimal place
            result += Numerical(10) ** -ndigits
            # may need to be tweaked slightly if the addition was inexact
            result = round(result, ndigits)
        else:
            result += 1 # same as 10 ** -0 for precision of zero digits
    return result

assert round_up(0.022499999999999999, 2) == 0.03
assert round_up(0.1111111111111000, 2) == 0.12

assert round_up(1.11, 2) == 1.11
assert round_up(1e308, 2) == 1e308
0

Here's a simple way to do it that I don't see in the other answers.

To round up to the second decimal place:

>>> n = 0.022499999999999999
>>> 
>>> -(-n//.01) * .01
0.03
>>> 

Other value:

>>> n = 0.1111111111111000
>>> 
>>> -(-n//.01) * .01
0.12
>>> 

With floats there's the occasional value with some minute imprecision, which can be corrected for if you're displaying the values for instance:

>>> n = 10.1111111111111000
>>> 
>>> -(-n//0.01) * 0.01
10.120000000000001
>>> 
>>> f"{-(-n//0.01) * 0.01:.2f}"
'10.12'
>>> 

A simple roundup function with a parameter to specify precision:

>>> roundup = lambda n, p: -(-n//10**-p) * 10**-p
>>> 
>>> # Or if you want to ensure truncation using the f-string method:
>>> roundup = lambda n, p: float(f"{-(-n//10**-p) * 10**-p:.{p}f}")
>>> 
>>> roundup(0.111111111, 2)
0.12
>>> roundup(0.111111111, 3)
0.112
Todd
  • 4,669
  • 1
  • 22
  • 30
0

I wrote simple function for round_up:

def round_up(number: float, ndigits: int):
    offset = 0.5
    if ndigits and ndigits > 0:
        offset = offset / (10 ** ndigits)
        return round(number + offset, ndigits)
   else:
       return round(number+offset)
milad_vayani
  • 398
  • 1
  • 4
  • 14
-1

The round funtion stated does not works for definate integers like :

a=8
round(a,3)
8.0
a=8.00
round(a,3)
8.0
a=8.000000000000000000000000
round(a,3)
8.0

but , works for :

r=400/3.0
r
133.33333333333334
round(r,3)
133.333

Morever the decimals like 2.675 are rounded as 2.67 not 2.68.
Better use the other method provided above.

Michael
  • 3,093
  • 7
  • 39
  • 83
Shikhar
  • 1
  • 2
  • This is not an answer but a comment. Please add this as a comment to the related answer – Ferro Jul 05 '20 at 04:52
-1

def round_decimals_up(number:float, decimals:int=2): """ Returns a value rounded up to a specific number of decimal places. """ if not isinstance(decimals, int): raise TypeError("decimal places must be an integer") elif decimals < 0: raise ValueError("decimal places has to be 0 or more") elif decimals == 0: return math.ceil(number)

factor = 10 ** decimals
return math.ceil(number * factor) / factor

round_decimals_up(0.022499999999999999)

Returns: 0.03

round_decimals_up(0.1111111111111000)

Returns: 0.12