20

How can I do the following rounding in python:

Round to the nearest 0.05 decimal

7,97 -> 7,95

6,72 -> 6,70

31,06 -> 31,05

36,04 -> 36,05

5,25 -> 5,25

Hope it makes sense.

FFmpegEnthusiast
  • 193
  • 1
  • 10
pkdkk
  • 3,905
  • 8
  • 44
  • 69

9 Answers9

32
def round_to(n, precision):
    correction = 0.5 if n >= 0 else -0.5
    return int( n/precision+correction ) * precision

def round_to_05(n):
    return round_to(n, 0.05)
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
fortran
  • 74,053
  • 25
  • 135
  • 175
  • `round_to_05(-1)` gives `-0.95` which doesn't seem like the right result to me. – David Webb Nov 24 '10 at 12:23
  • 1
    true, I was thinking in natural numbers... I'll fix it. – fortran Nov 24 '10 at 17:03
  • `correction` is not needed if we use `round()` to replace `int()` like this `round(n / precision) * precision`. Then the wrapper func is not needed as well since this is really a simple line of code. However it does have some flaw, such as `round(0.275/.05)*.05 == 0.30000000000000004`. To avoid that, maybe we could use `len(str(.05).split('.')[-1])` to get the number of decimal digits and use `round()` again to change `0.30000000000000004` to `0.30`. – mo-han Apr 04 '21 at 03:11
23
def round05(number):
    return (round(number * 20) / 20)

Or more generically:

def round_to_value(number,roundto):
    return (round(number / roundto) * roundto)

The only problem is because you're using floats you won't get exactly the answers you want:

>>> round_to_value(36.04,0.05)
36.050000000000004
Community
  • 1
  • 1
David Webb
  • 190,537
  • 57
  • 313
  • 299
  • 1
    `decimal` is the name of a module in the python standard library, so you might want to avoid using that name. – martineau Nov 24 '10 at 10:32
  • @martineau - You're right and it's also a bad name as the second argument doesn't have to be a decimal, you can use the function to round to whole number too, e.g. `round_to_value(36.04,5)` gives `35.0`. – David Webb Nov 24 '10 at 12:26
  • +1 because this is currently the only answer that provides a generalized solution (which is, not surprisingly, generally better IMHO). – martineau Nov 24 '10 at 15:47
  • Using "decimal" when you mean "fraction" is definitely not an aid to understanding. – John Machin Nov 24 '10 at 20:31
4

There we go.

round(VALUE*2.0, 1) / 2.0

regards

SubniC
  • 9,807
  • 4
  • 26
  • 33
3

Here's a one liner

def roundto(number, multiple):
   return number+multiple/2 - ((number+multiple/2) % multiple)
rafaelvalle
  • 6,683
  • 3
  • 34
  • 36
3

Using lambda function:

>>> nearest_half = lambda x: round(x * 2) / 2
>>> nearest_half(5.2)
5.0
>>> nearest_half(5.25)
5.5
>>> nearest_half(5.26)
5.5
>>> nearest_half(5.5)
5.5
>>> nearest_half(5.75)
6.0
Sagun Shrestha
  • 1,188
  • 10
  • 23
2

To round it to exactly how you want to:

>>> def foo(x, base=0.05):
...     return round(base*round(x/base), 2)

>>> foo(5.75)
5.75
>>> foo(5.775)
5.8
>>> foo(5.77)
5.75
>>> foo(7.97)
7.95
>>> foo(6.72)
6.7
>>> foo(31.06)
31.05
>>> foo(36.04)
36.05
>>> foo(5.25)
5.25
shank22
  • 159
  • 2
  • 12
0

I faced the same problem and as I didn't find the ultimate solution to this, here's mine.

Firs of all the main part(which was answered before):

def round_to_precision(x, precision):
    # This correction required due to float errors and aims to avoid cases like:
    # 100.00501 / 0.00001 = 10000500.999999998
    # It has a downside as well - it may lead to vanishing the difference for case like
    # price = 100.5 - (correction - correction/10), precision = 1 => 101 not 100
    # 5 decimals below desired precision looks safe enough to ignore
    correction = 1e-5 if x > 0 else -1e-5
    result = round(x / precision + correction) * precision
    return round(result, find_first_meaningful_decimal(precision))

The only tricky part here was that find_first_meaningful_decimal, which I've implemented like this:

def find_first_meaningful_decimal(x):
    candidate = 0
    MAX_DECIMAL = 10
    EPSILON = 1 / 10 ** MAX_DECIMAL
    while round(x, candidate) < EPSILON:
        candidate +=1
        if candidate > MAX_DECIMAL:
            raise Exception('Number is too small: {}'.format(x))
    if int(x * 10 ** (candidate + 1)) == 5:
        candidate += 1
    return candidate


print(round_to_precision(129.950002, 0.0005))
print(round_to_precision(-129.95005, 0.0001))

129.9505
-129.9501
Nik
  • 315
  • 4
  • 9
0
import numpy as np

for Roundup

df['a']=(df["a"]*2).apply(np.ceil)/2

for Round

df['a']=(df["a"]*2).apply(np.floor)/2

This is working with columns for roundup 0.5 using numpy...

Jaimil Patel
  • 1,301
  • 6
  • 13
-1

An extension of the accepted answer.

def round_to(n, precision):
    correction = precision if n >= 0 else -precision
    return round(int(n/precision+correction)*precision, len(str(precision).split('.')[1]))


test_cases = [101.001, 101.002, 101.003, 101.004, 101.005, 101.006, 101.007, 101.008, 101.009]
[round_to(-x, 0.003) for x in test_cases]
[-101.001, -101.001, -101.001, -101.004, -101.004, -101.004, -101.007, -101.007, -101.007]
eebbesen
  • 5,070
  • 8
  • 48
  • 70