1

I want to round up numbers in Python to a specified decimal.

For example, if I choose

(2.1, 0.2) -->  I want to get 2.2 
(2.2, 0.2) -->  I want to get 2.2 
(3.6, 0.5) -->  I want to get 4.0
(3.5, 0.5) -->  I want to get 3.5
Jeroen
  • 801
  • 6
  • 20
  • Does this answer your question? [Round integers to the nearest 10](https://stackoverflow.com/questions/3348825/round-integers-to-the-nearest-10) – AlexisG Oct 20 '20 at 16:13
  • 1
    Are you sure about your third output? I expect it to be 3.5 – mathfux Oct 20 '20 at 16:46
  • @mathfux I suppose that the second value is the threshold on which he\she decide to round up. 3.6 - 3 = 0.6 > 0.5. – Nikaido Oct 20 '20 at 16:55
  • @mathfux check my solution – Nikaido Oct 20 '20 at 16:56
  • @Nikaido It's not mentioned in a question. It could be in a different way: `rounder(x, y)` is some multiple of `y` that is closest to `x`. If there are two such multiples, choose the one that has a higher absolute value. – mathfux Oct 20 '20 at 17:01
  • @mathfux, yeah, you are right. I based my answer on the output btw – Nikaido Oct 20 '20 at 17:05
  • My guess is: if the floating part of the number is <= of the rounding number, then cap the value to that float, else get the ceiling – Nikaido Oct 20 '20 at 17:07
  • It's a bad idea to guess smth at all. Every question should be [well defined](https://en.wikipedia.org/wiki/Well-defined). If it's not, you need to point out what are different ways to interpret it. – mathfux Oct 20 '20 at 17:11
  • @mathfux, Yeah I agree on that. Unfortunately every one here has answered assuming what the user wanted – Nikaido Oct 20 '20 at 17:14

3 Answers3

2

probably not the most efficient, but working with numpy

I am assuming that you are working with not too much decimals

import numpy as np 

def rounder(num, round_):
   int_p = np.floor(num)
   float_p = num - int_p
   print(float_p) # this print is meant to show the floating point problem
   diff  = float_p - round_
   if diff <= 0 or -0.00001 <diff< 0.00001:
       return int_p + round_
   else:
       return np.ceil(num)

 list_ = [(2.1, 0.2), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5)]

 for num, round_ in list_:
     print("({}, {})".format(num, round_), rounder(num, round_))

 
 # (2.1, 0.2) 2.2
 # (2.2, 0.2) 2.2
 # (3.6, 0.5) 4.0
 # (3.5, 0.5) 3.5

unfortunately there are problems with the representation of the floating point that is not so precise. That's the reason why I wrote another condition in the if else -0.00001 <diff< 0.00001 that means, if the difference is not so high (near zero) than it is basically zero

New algorithm as requested from op

import numpy as np 

def rounder(num, round_):
  int_p = np.floor(num)
  new_value = int_p
  while new_value < num:
    new_value = round(new_value + round_, 4)
  return new_value


list_ = [(2.1, 0.2), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5), (0.178, 0.1)]

for num, round_ in list_:
    print("({}, {})".format(num, round_), rounder(num, round_))


# (2.1, 0.2) 2.2
# (2.2, 0.2) 2.2
# (3.6, 0.5) 4.0
# (3.5, 0.5) 3.5
# (0.178, 0.1) 0.2
Nikaido
  • 4,443
  • 5
  • 30
  • 47
  • After a second check it doesn't give the right result for: `print(rounder(0.178,0.1))` which should give 0.2 but gives 1.0 – Jeroen Oct 21 '20 at 13:02
  • @Jeroen let me check a sec – Nikaido Oct 21 '20 at 13:21
  • @Jeroen then I am not understanding what you want exactly. In your example, for the value (3.6, 0.5) why do you get 4? – Nikaido Oct 21 '20 at 13:30
  • for what I did understand, for the value (0.178, 0.1) I should get 1 – Nikaido Oct 21 '20 at 13:31
  • For (3.6, 0.5) I indeed want to have (4.0). It is rounded up the next value as a multiplication of 0.5. Then For (0.178, 0.1) we have possible candidates (0,0.1,0.2,0.3) --> so this would be 0.2. Only when the number is equal to a multiplication of r ound_, I want to maintain the number – Jeroen Oct 21 '20 at 13:40
  • @Jeroen check it now – Nikaido Oct 21 '20 at 15:05
  • Sorry if I am not clear, but if I use (9.3, 0.4) I get 9.4, where I expect 9.6. The closest multiplications of 0.4 with an integer to 9.3 are 9.2 and 9.6. The upper limit is 9.6. Only when the upper limit is exactly the original number, I want to return the original number. – Jeroen Oct 22 '20 at 12:58
0

What I wanted was:

def rounder(num, round_):
    val = num // round_   
    if val*round_  == num:
        return num
    else:
        return val*round_ + round_ 
    
list_ = [(9.3, 0.4), (2.2, 0.2), (3.6, 0.5), (3.5, 0.5), (0.178, 0.1)]

for num, round_ in list_:
    print("({}, {})".format(num, round_), rounder(num, round_))
Jeroen
  • 801
  • 6
  • 20
-1

Assume that rounder(x, y) is some multiple of y that is closest to x. If there are two such multiples, choose the one that has a higher absolute value.

Python has an issue of rounding halves:

>>> round(10.5)
10
>>> np.round(10.5)
10.0

Iterable case

Borrowing from this simple fix, I'll extend it for OP's question

import math
def rounder(x, y):
    r = x/y
    if (r - int(r) >= 0.5):
        return y * math.ceil(r)
    else:
        return y * math.floor(r)

Sample run:

>>> rounder(2.1, 0.2)
2.2
>>> rounder(2.2, 0.2)
2.2
>>> rounder(3.6, 0.5)
3.5
>>> rounder(3.5, 0.5)
3.5

This is designed to call it multiple times on items, usually in a process of some iterations.

Vectorised case

It's also possible to rewrite it in order to work in vectorised way:

def rounder(x, y):
    r = x/y
    np.where(r-r//1 >= 0.5, y * np.ceil(r), y * np.floor(r))

>>> rounder(np.array([2.1,2.2,3.6,3.5]), np.array([0.2, 0.2, 0.5, 0.5]))
array([2.2, 2.2, 3.5, 3.5])
mathfux
  • 5,759
  • 1
  • 14
  • 34