9

I want the most Pythonic way to round numbers just like Javascript does (through Math.round()). They're actually slightly different, but this difference can make huge difference for my application.

Using round() method from Python 3:

// Returns the value 20
x = round(20.49)

// Returns the value 20
x = round(20.5)

// Returns the value -20
x = round(-20.5)

// Returns the value -21
x = round(-20.51)

Using Math.round() method from Javascript*:

// Returns the value 20
x = Math.round(20.49);

// Returns the value 21
x = Math.round(20.5);

// Returns the value -20
x = Math.round(-20.5);

// Returns the value -21
x = Math.round(-20.51);

Thank you!

References:

PM 2Ring
  • 54,345
  • 6
  • 82
  • 182
Paladini
  • 4,522
  • 15
  • 53
  • 96
  • 3
    Please specify which version of python you are using, as this may be different between python 2 and python 3. – khelwood Jan 19 '16 at 12:21
  • First of all, why the negative point without any explanation about that? And thank you @khelwood, I'll provide the information. – Paladini Jan 19 '16 at 12:22
  • 3
    [This question](http://stackoverflow.com/q/10825926/3005167) and its answers might shade light on the situation. – MB-F Jan 19 '16 at 12:24
  • I've updated the question to include the Python version! – Paladini Jan 19 '16 at 12:26
  • 1
    Thank you for the linked question, @kazemakase . Now I understand why Python3 choose to round numbers this way. – Paladini Jan 19 '16 at 12:35
  • FWIW, asking a question that implies that Python is inferior in some way to JavaScript may not be well-received by many Pythonistas. :) – PM 2Ring Jan 19 '16 at 12:43
  • @PM2Ring Seems to you that I'm arguing that? I'm moving a Javascript code to Python, I NEED THE EXACT behavior in both cases, why this implies that Python is worst than JS? The project involves the measurement of metrics like temperature and pressure, I can't change the way we normalize / convert the data to the correct value. It's simple as that. – Paladini Jan 19 '16 at 13:50
  • @FernandoPaladini: That makes perfect sense. My comment wasn't serious, hence the smilie. Still, plenty of Pythonistas are not big fans of JavaScript... – PM 2Ring Jan 19 '16 at 14:31

3 Answers3

11
import math
def roundthemnumbers(value):
    x = math.floor(value)
    if (value - x) < .50:
        return x
    else:
        return math.ceil(value)

Haven't had my coffee yet, but that function should do what you need. Maybe with some minor revisions.

Paladini
  • 4,522
  • 15
  • 53
  • 96
Jesse Pardue
  • 180
  • 1
  • 1
  • 9
  • 1
    Thank you for the answer too, @JessePardue . I haven't named your question as the best one because your solution is slightly more complex than the other. Anyway, thank you for the answer! – Paladini Jan 19 '16 at 12:42
  • Just two questions: (a) Why are you returning the values inside a parenthesis? (b) Why you store `y` before returning it? Isn't better return the value directly? – Paladini Jan 19 '16 at 12:43
  • @FernandoPaladini: In Python, `math.ceil(value)` creates a float object with the required value, doing `y = math.ceil(value)` merely binds that object to the name `y` (which is a fairly efficient operation); it doesn't create a copy of the value, so it's not as inefficient as it might first appear (or as it would be in most other languages). – PM 2Ring Jan 19 '16 at 12:51
  • Alright, the corrections have been made and the function name--as you can see--has been made intuitive, lol. – Jesse Pardue Jan 19 '16 at 12:55
  • 4
    I think you want `< 0.5` instead of `<= 0.5`: the exact halfway cases should round up instead of down. – Mark Dickinson Jan 19 '16 at 13:04
  • Yes, @MarkDickinson is right about the comment above. Should be `< 0.5` instead of `<= 0.5`. – Paladini Jan 19 '16 at 13:55
  • 1
    It's worth pointing out that under usual IEEE 754 rules (i.e., round-ties-to-even rounding mode), this function gives the correct result for all inputs, though that's not completely obvious from the implementation. The potential problem is that `value - x` may not be exactly representable, and the rounding step implicit in that subtraction could potentially alter the comparison result. However, a closer analysis shows that `value - x` *is* exactly representable for `x` not in `(-0.5, 0)`, and for values in `(-0.5, 0)` we always have `value - x >= 0.5`, even after taking rounding into account. – Mark Dickinson Jan 19 '16 at 16:12
  • 1
    Actually, ignore the bit about the rounding mode; this should work for any of the four standard IEEE 754 rounding modes. The key observation is that in the troublesome interval, the exact value of `value - x` is strictly greater than `0.5`, and it follows that the rounded value of `value - x` is greater than or equal to `0.5`, no matter what the rounding mode. So `value` will always end up being rounded up, as it should be. – Mark Dickinson Jan 19 '16 at 16:21
7

The behavior of Python's round function changed between Python 2 and Python 3. But it looks like you want the following, which will work in either version:

math.floor(x + 0.5)

This should produce the behavior you want.

Tom Karzes
  • 22,815
  • 2
  • 22
  • 41
  • 3
    Note that there are some edge case inputs that aren't properly handled by this solution: examples are `0.49999999999999994` (which produces `1.0` instead of `0.0`), `5000000000000001.0` (which produces `5000000000000002.0`), `-0.3` (which produces `0.0` instead of `-0.0`). If you care about those edge cases, something along the lines of @JessePardue's solution is better. – Mark Dickinson Jan 19 '16 at 12:53
  • Wooow, thanks for the advise, @MarkDickinson . Yes, the first and the second situation are very important to me, I really care about that. I'll change my solution to the one pointed by JessePardue. Again, thank you for the advise! – Paladini Jan 19 '16 at 13:52
1

Instead of using round() function in python you can use the floor function and ceil function in python to accomplish your task.

floor(x+0.5)

or

ceil(x-0.5)