220

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
  • 14,486
  • 4
  • 43
  • 54
Pydev UA
  • 4,983
  • 6
  • 39
  • 52

20 Answers20

413

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
  • 14,486
  • 4
  • 43
  • 54
Alok Singhal
  • 88,099
  • 18
  • 124
  • 155
  • 6
    If only integers and rounding down, then you can also just do `x // base * base` – Tjorriemorrie Dec 20 '16 at 00:59
  • 9
    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
68

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,135
  • 8
  • 19
  • 6
    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
24

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,199
  • 24
  • 35
20

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
  • 952
  • 9
  • 7
18
def round_to_next5(n):
    return n + (5 - n) % 5
Andy Wong
  • 2,821
  • 1
  • 18
  • 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,147
  • 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
  • 9,662
  • 10
  • 42
  • 67
Aku
  • 527
  • 4
  • 16
  • Please don't try to bypass new user restrictions by posting a comment as an answer. [Restrictions exist for a reason](//meta.stackexchange.com/a/214174/295637). Take this into account as a possible reason as to [Why and how are some answers deleted?](//stackoverflow.com/help/deleted-answers) – Blue Oct 20 '18 at 05:12
  • 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
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,511
  • 21
  • 30
7
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
  • 671
  • 1
  • 10
  • 16
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

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
  • 47,741
  • 6
  • 96
  • 115
  • 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,030
  • 21
  • 100
  • 124
vijay
  • 49
  • 1
  • 2
2

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

def myround(x, base=5):
    roundcontext = decimal.Context(rounding=decimal.ROUND_HALF_UP)
    decimal.setcontext(roundcontext)
    return int(base *float(decimal.Decimal(x/base).quantize(decimal.Decimal('0'))))

As per documentation other 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), or
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).

Piotr Siejda
  • 155
  • 8
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,353
  • 10
  • 21
2

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

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

What about this:

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

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);
  }
}
Ege Merey
  • 19
  • 3
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

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,170
  • 2
  • 23
  • 32
-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,078
  • 12
  • 73
  • 92