251

Is there a built-in function that can round like the following?

10 -> 10
12 -> 10
13 -> 15
14 -> 15
16 -> 15
18 -> 20
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Djangonaut
  • 5,511
  • 7
  • 40
  • 53

23 Answers23

464

I don't know of a standard function in Python, but this works for me:

Python 3

def myround(x, base=5):
    return base * round(x/base)

It is easy to see why the above works. You want to make sure that your number divided by 5 is an integer, correctly rounded. So, we first do exactly that (round(x/5)), and then since we divided by 5, we multiply by 5 as well.

I made the function more generic by giving it a base parameter, defaulting to 5.

Python 2

In Python 2, float(x) would be needed to ensure that / does floating-point division, and a final conversion to int is needed because round() returns a floating-point value in Python 2.

def myround(x, base=5):
    return int(base * round(float(x)/base))
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
  • 7
    If only integers and rounding down, then you can also just do `x // base * base` – Tjorriemorrie Dec 20 '16 at 00:59
  • 10
    this is me being paranoid but I prefer to use `floor()` and `ceil()` rather than casting: `base * floor(x/base)` – user666412 Apr 05 '17 at 16:06
  • 3
    @user666412 `math.floor` and `math.ceil` don't allow use with a custom base, so the preference is irrelevant. – Asclepius Sep 23 '19 at 19:18
  • this works great and rounds to an integer. If you want to round to floats, just remove the 'int' from the function. I wanted to round to values multiple of 0.05, and worked perfectly. – rod_CAE Oct 14 '21 at 13:17
  • 1
    In case others stumble upon this, the suggested Python 3 way produces false results. `m = 2312**9; n = 3; m * round(n / m) == 1887515243828655024291056713728` where as using the Python 2 way in Py3, casting `x` or `base` as a float you get `m = 2312**9; n = 3; m * round(float(n) / m) == 1887515243828654813184824180736` – bnlucas Sep 15 '22 at 03:17
  • @bnlucas wow, thank you! It seems like in Python 3, `/` converts both operands to `float` and loses precision. Even in Python 2, due to the size of numbers involved, the result isn't actually correct. For example, using `float()` explicitly in Python 3: `m - m * round(float(n) / m)` gives a large residual instead of <= 3. – Alok Singhal Sep 26 '22 at 03:57
  • @bnlucas for large numbers, I'd recommend using `divmod()` and manually doing this. E.g., `m = 2312**9; n = 3; q, r = divmod(m, n); if r > n // 2: q+= 1; q *= n;` or something like that. – Alok Singhal Sep 26 '22 at 03:58
81

For rounding to non-integer values, such as 0.05:

def myround(x, prec=2, base=.05):
  return round(base * round(float(x)/base),prec)

I found this useful since I could just do a search and replace in my code to change "round(" to "myround(", without having to change the parameter values.

CCKx
  • 1,303
  • 10
  • 22
  • 10
    You can use: `def my_round(x, prec=2, base=0.05): return (base * (np.array(x) / base).round()).round(prec)` which accepts numpy arrays as well. – saubhik May 24 '18 at 18:21
  • print(myround(10.205)) generate 10.24 print(myround(10.135)) generate 10.16 – Ramesh Patel Mar 24 '23 at 01:59
  • float typecasting no loger req if using >=py3.5 . find more detailed description at PEP 238: Changing the Division Operator. – kappa101 Jul 20 '23 at 06:38
26

It's just a matter of scaling

>>> a=[10,11,12,13,14,15,16,17,18,19,20]
>>> for b in a:
...     int(round(b/5.0)*5.0)
... 
10
10
10
15
15
15
15
15
20
20
20
amo-ej1
  • 3,279
  • 26
  • 35
19

Removing the 'rest' would work:

rounded = int(val) - int(val) % 5

If the value is aready an integer:

rounded = val - val % 5

As a function:

def roundint(value, base=5):
    return int(value) - int(value) % int(base)
hgdeoro
  • 1,030
  • 10
  • 7
17
def round_to_next5(n):
    return n + (5 - n) % 5
Andy Wong
  • 3,676
  • 1
  • 21
  • 18
10

round(x[, n]): values are rounded to the closest multiple of 10 to the power minus n. So if n is negative...

def round5(x):
    return int(round(x*2, -1)) / 2

Since 10 = 5 * 2, you can use integer division and multiplication with 2, rather than float division and multiplication with 5.0. Not that that matters much, unless you like bit shifting

def round5(x):
    return int(round(x << 1, -1)) >> 1
pwdyson
  • 1,177
  • 7
  • 14
  • 1
    +1 for showing us that round() can handle rounding to multiples other than 1.0, including higher values. (Note, however, that the bit-shifting approach won't work with floats, not to mention it's much less readable to most programmers.) – Peter Hansen Feb 16 '10 at 14:50
  • 1
    @Peter Hansen thanks for the +1. Need to have an int(x) for the bit shifting to work with floats. Agreed not the most readable and I wouldn't use it myself, but I did like the "purity" of it only involving 1's and not 2's or 5's. – pwdyson Feb 16 '10 at 22:26
10

Sorry, I wanted to comment on Alok Singhai's answer, but it won't let me due to a lack of reputation =/

Anyway, we can generalize one more step and go:

def myround(x, base=5):
    return base * round(float(x) / base)

This allows us to use non-integer bases, like .25 or any other fractional base.

Seth
  • 10,198
  • 10
  • 45
  • 68
Aku
  • 526
  • 4
  • 17
  • 3
    This works as an answer in itself, though. I used it, without defining it as a function: y = base * round(float(x) / base). It works as long as you have already defined x and base. Note that this answer got seven upvotes. – jason-hernandez-73 Oct 15 '20 at 20:04
9
def round_up_to_base(x, base=10):
    return x + (base - x) % base

def round_down_to_base(x, base=10):
    return x - (x % base)

which gives

for base=5:

>>> [i for i in range(20)]
[0, 1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [round_down_to_base(x=i, base=5) for i in range(20)]
[0, 0,  0,  0,  0,  5,  5,  5,  5,  5,  10, 10, 10, 10, 10, 15, 15, 15, 15, 15]

>>> [round_up_to_base(x=i, base=5) for i in range(20)]
[0, 5,  5,  5,  5,  5,  10, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20]

for base=10:

>>> [i for i in range(20)]
[0, 1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
>>> [round_down_to_base(x=i, base=10) for i in range(20)]
[0, 0,  0,  0,  0,  0,  0,  0,  0,  0,  10, 10, 10, 10, 10, 10, 10, 10, 10, 10]

>>> [round_up_to_base(x=i, base=10) for i in range(20)]
[0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20]

tested in Python 3.7.9

KiriSakow
  • 957
  • 1
  • 12
  • 22
8

Use:

>>> def round_to_nearest(n, m):
        r = n % m
        return n + m - r if r + r >= m else n - r

It does not use multiplication and will not convert from/to floats.

Rounding to the nearest multiple of 10:

>>> for n in range(-21, 30, 3): print('{:3d}  =>  {:3d}'.format(n, round_to_nearest(n, 10)))
-21  =>  -20
-18  =>  -20
-15  =>  -10
-12  =>  -10
 -9  =>  -10
 -6  =>  -10
 -3  =>    0
  0  =>    0
  3  =>    0
  6  =>   10
  9  =>   10
 12  =>   10
 15  =>   20
 18  =>   20
 21  =>   20
 24  =>   20
 27  =>   30

As you can see, it works for both negative and positive numbers. Ties (e.g. -15 and 15) will always be rounded upwards.

A similar example that rounds to the nearest multiple of 5, demonstrating that it also behaves as expected for a different "base":

>>> for n in range(-21, 30, 3): print('{:3d}  =>  {:3d}'.format(n, round_to_nearest(n, 5)))
-21  =>  -20
-18  =>  -20
-15  =>  -15
-12  =>  -10
 -9  =>  -10
 -6  =>   -5
 -3  =>   -5
  0  =>    0
  3  =>    5
  6  =>    5
  9  =>   10
 12  =>   10
 15  =>   15
 18  =>   20
 21  =>   20
 24  =>   25
 27  =>   25
wouter bolsterlee
  • 3,879
  • 22
  • 30
4

Modified version of divround :-)

def divround(value, step, barrage):
    result, rest = divmod(value, step)
    return result*step if rest < barrage else (result+1)*step
3

In case someone needs "financial rounding" (0.5 rounds always up):

from decimal import ROUND_HALF_UP, Decimal, localcontext

def myround(x, base: int = 5):
    # starting with Python 3.11:
    # with localcontext(rounding=decimal.ROUND_HALF_UP):
    with localcontext() as ctx:
        ctx.rounding = ROUND_HALF_UP
        return base * int(decimal.Decimal(x / base).quantize(Decimal('0')))

As per documentation the rounding options are:

  • ROUND_CEILING (towards Infinity)
  • ROUND_DOWN (towards zero)
  • ROUND_FLOOR (towards -Infinity)
  • ROUND_HALF_DOWN (to nearest with ties going towards zero)
  • ROUND_HALF_EVEN (to nearest with ties going to nearest even integer)
  • ROUND_HALF_UP (to nearest with ties going away from zero)
  • ROUND_UP (away from zero)
  • ROUND_05UP (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)

By default Python uses ROUND_HALF_EVEN as it has some statistical advantages (the rounded results are not biased).

Cristian Ciupitu
  • 20,270
  • 7
  • 50
  • 76
Piotr Siejda
  • 197
  • 8
  • 1
    Instead of setting the decimal context permanently as a side effect when the function is called, you should probably set it explicitly beforehand or even better use a [local context](https://docs.python.org/3/library/decimal.html#decimal.localcontext) temporarily. – Cristian Ciupitu Jul 02 '22 at 12:22
3

For integers and with Python 3:

def divround_down(value, step):
    return value//step*step


def divround_up(value, step):
    return (value+step-1)//step*step

Producing:

>>> [divround_down(x,5) for x in range(20)]
[0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 15, 15, 15, 15, 15]
>>> [divround_up(x,5) for x in range(20)]
[0, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 15, 15, 15, 15, 15, 20, 20, 20, 20]
Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125
  • Hi, what do you think of my algorithm? Which is like yours but looks simpler https://stackoverflow.com/a/65725123/4883320 – KiriSakow Jan 14 '21 at 18:48
  • Hi @KiriSakow -- your solution looks good to me. To be honest, I don't know why I posted an answer for that question myself -- especially why I posted _that_ answer, which far from being excellent :/ – Sylvain Leroux Jan 14 '21 at 23:47
2

Next multiple of 5

Consider 51 needs to be converted to 55:

code here

mark = 51;
r = 100 - mark;
a = r%5;
new_mark = mark + a;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
vijay
  • 49
  • 1
  • 2
2

Another way to do this (without explicit multiplication or division operators):

def rnd(x, b=5):
    return round(x + min(-(x % b), b - (x % b), key=abs))
cosmic_inquiry
  • 2,557
  • 11
  • 23
2

No one actually wrote this yet I guess but you can do:

round(12, -1) --> 10
round(18, -1) --> 20
Dome271
  • 81
  • 2
  • 11
1

What about this:

 def divround(value, step):
     return divmod(value, step)[0] * step
1

I needed to round down to the preceding 5.

Example 16 rounds down to 15 or 19 rounds down to 15

Here's the code used

    def myround(x,segment):
        preRound = x / segment
        roundNum = int(preRound)
        segVal = segment * roundNum
        return segVal
Ty Palm
  • 11
  • 1
0

Here is my C code. If I understand it correctly, it should supposed to be something like this;

#include <stdio.h>

int main(){
int number;

printf("Enter number: \n");
scanf("%d" , &number);

if(number%5 == 0)
    printf("It is multiple of 5\n");
else{
    while(number%5 != 0)
        number++;
  printf("%d\n",number);
  }
}

and this also rounds to nearest multiple of 5 instead of just rounding up;

#include <stdio.h>

int main(){
int number;

printf("Enter number: \n");
scanf("%d" , &number);

if(number%5 == 0)
    printf("It is multiple of 5\n");
else{
    while(number%5 != 0)
        if (number%5 < 3)
            number--;
        else
        number++;
  printf("nearest multiple of 5 is: %d\n",number);
  }
}
XxXeGeXxX
  • 17
  • 3
0

An addition to accepted answer, to specify rounding up or down to nearest 5-or-whatever

import math

def my_round(x, base, down = True):
    return base * math.floor(x/base) + (not down) * base
Jon
  • 2,280
  • 2
  • 25
  • 33
0

A solution that works only with ints (it accepts floats, but the rounding behaves as if the decimal component doesn't exist), but unlike any solution relying on temporary conversion to float (all the math.floor/math.ceil-based solutions, all the solutions using /, most solutions using round), it works for arbitrarily huge int inputs, never losing precision, never raising exceptions or resulting in infinity values.

It's an adaptation of the simplest solution for rounding down to the next lower multiple of a number:

def round_to_nearest(num, base=5):
    num += base // 2
    return num - (num % base)

The round down recipe it's based on is just:

def round_down(num, base=5):
    return num - (num % base)

the only change is that you add half the base to the number ahead of time so it rounds to nearest. With exact midpoint values, only possible with even bases, rounding up, so round_to_nearest(3, 6) will round to 6 rather than 0, while round_to_nearest(-3, 6) will round to 0 rather than -6. If you prefer midpoint values round down, you can change the first line to num += (base - 1) // 2.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
0
from math import isclose

def myPrice (p1,p2):
    return isclose(p1, p2, rel_tol=0.05)

print(myPrice(50.10,50.20)) 

To set a tolerance of 5%, pass rel_tol=0.05. The default tolerance is 1e-09

Ramesh Patel
  • 108
  • 2
  • 7
-1

I find this one to be negligibly slower than the answer by @mkrieger1 and @Alok Singhal but it is more explicit about the rounding behavior and easier to modify or extend.

def round_up_to_5(num):
    rounded_num = math.ceil(num / 5) * 5
    return int(rounded_num)
Ignis
  • 9
  • 2
-4

You can “trick” int() into rounding off instead of rounding down by adding 0.5 to the number you pass to int().

Uri Agassi
  • 36,848
  • 14
  • 76
  • 93