329

I've been trying to round long float numbers like:

32.268907563;
32.268907563;
31.2396694215;
33.6206896552;
...

With no success so far. I tried math.ceil(x), math.floor(x) (although that would round up or down, which is not what I'm looking for) and round(x) which didn't work either (still float numbers).

What could I do?

Code:

for i in widthRange:
    for j in heightRange:
        r, g, b = rgb_im.getpixel((i, j))
        h, s, v = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)
        h = h * 360
        int(round(h))
        print(h)
Neuron
  • 5,141
  • 5
  • 38
  • 59
vandernath
  • 3,665
  • 3
  • 15
  • 24

16 Answers16

531

TL;DR:

round(x)

will round it and change it to integer.

You are not assigning round(h) to any variable. When you call round(h), it returns the integer number but does nothing else; you have to change that line for:

h = round(h)

to assign the new value to h.


As @plowman said in the comments, Python's round() doesn't work as one would normally expect, and that's because the way the number is stored as a variable is usually not the way you see it on screen. There are lots of answers that explain this behavior.

One way to avoid this problem is to use the Decimal as stated by this answer.

In order for this answer to work properly without using extra libraries it would be convenient to use a custom rounding function. I came up with the following solution, that as far as I tested avoided all the storing issues. It is based on using the string representation, obtained with repr() (NOT str()!). It looks hacky but it was the only way I found to solve all the cases. It works with both Python2 and Python3.

def proper_round(num, dec=0):
    num = str(num)[:str(num).index('.')+dec+2]
    if num[-1]>='5':
        return float(num[:-2-(not dec)]+str(int(num[-2-(not dec)])+1))
    return float(num[:-1])

Tests:

>>> print(proper_round(1.0005,3))
1.001
>>> print(proper_round(2.0005,3))
2.001
>>> print(proper_round(3.0005,3))
3.001
>>> print(proper_round(4.0005,3))
4.001
>>> print(proper_round(5.0005,3))
5.001
>>> print(proper_round(1.005,2))
1.01
>>> print(proper_round(2.005,2))
2.01
>>> print(proper_round(3.005,2))
3.01
>>> print(proper_round(4.005,2))
4.01
>>> print(proper_round(5.005,2))
5.01
>>> print(proper_round(1.05,1))
1.1
>>> print(proper_round(2.05,1))
2.1
>>> print(proper_round(3.05,1))
3.1
>>> print(proper_round(4.05,1))
4.1
>>> print(proper_round(5.05,1))
5.1
>>> print(proper_round(1.5))
2.0
>>> print(proper_round(2.5))
3.0
>>> print(proper_round(3.5))
4.0
>>> print(proper_round(4.5))
5.0
>>> print(proper_round(5.5))
6.0
>>> 
>>> print(proper_round(1.000499999999,3))
1.0
>>> print(proper_round(2.000499999999,3))
2.0
>>> print(proper_round(3.000499999999,3))
3.0
>>> print(proper_round(4.000499999999,3))
4.0
>>> print(proper_round(5.000499999999,3))
5.0
>>> print(proper_round(1.00499999999,2))
1.0
>>> print(proper_round(2.00499999999,2))
2.0
>>> print(proper_round(3.00499999999,2))
3.0
>>> print(proper_round(4.00499999999,2))
4.0
>>> print(proper_round(5.00499999999,2))
5.0
>>> print(proper_round(1.0499999999,1))
1.0
>>> print(proper_round(2.0499999999,1))
2.0
>>> print(proper_round(3.0499999999,1))
3.0
>>> print(proper_round(4.0499999999,1))
4.0
>>> print(proper_round(5.0499999999,1))
5.0
>>> print(proper_round(1.499999999))
1.0
>>> print(proper_round(2.499999999))
2.0
>>> print(proper_round(3.499999999))
3.0
>>> print(proper_round(4.499999999))
4.0
>>> print(proper_round(5.499999999))
5.0

Finally, the corrected answer would be:

# Having proper_round defined as previously stated
h = int(proper_round(h))

Tests:

>>> proper_round(6.39764125, 2)
6.31 # should be 6.4
>>> proper_round(6.9764125, 1)
6.1  # should be 7

The gotcha here is that the dec-th decimal can be 9 and if the dec+1-th digit >=5 the 9 will become a 0 and a 1 should be carried to the dec-1-th digit.

If we take this into consideration, we get:

def proper_round(num, dec=0):
    num = str(num)[:str(num).index('.')+dec+2]
    if num[-1]>='5':
      a = num[:-2-(not dec)]       # integer part
      b = int(num[-2-(not dec)])+1 # decimal part
      return float(a)+b**(-dec+1) if a and b == 10 else float(a+str(b))
    return float(num[:-1])

In the situation described above b = 10 and the previous version would just concatenate a and b which would result in a concatenation of 10 where the trailing 0 would disappear. This version transforms b to the right decimal place based on dec, as a proper carry.

Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
francisco sollima
  • 7,952
  • 4
  • 22
  • 38
  • 4
    print("4.5)", int(round(4.5))) # gave me 4 print("5.5)", int(round(5.5))) # gave me 6 :,( – Komm Jun 11 '17 at 05:10
  • It's related to the Python version. It gives me 5 and 6 using Python 2.7.9 and, as you said, 4 and 6 using Python 3.4.2 – francisco sollima Jun 15 '17 at 13:25
  • 1
    Worth noting: this solution does not round in the way you probably expect. For example, `int(round(4.5))` rounds down to 4 while `int(round(4.500001))` correctly rounds to 5. – plowman Aug 08 '19 at 00:34
  • 5
    If youn want an integer then `round(x)` is sufficient in Python 3.6.2 (and maybe lower versions as well). The result is already of type int. Note: `round(x, n)` will be of type float. – Elmex80s Oct 31 '19 at 10:58
  • 1
    This doesn't work for 112439.50093565206. It gives o/p -> 11253.0. Damn weird..!!!! – ajin Jan 15 '20 at 06:52
  • I think even with the update this has errors. I get unusual results for `proper_round(0.0005,4)` and `proper_round(0.9995,4)`. I get something even stranger for `proper_round(0.9995,5)` – craq Jun 04 '20 at 23:19
  • 2
    proper_round(19.5) gives very wrong answers for both versions of the custom function. for the first version it returns 110.0 and for the second it returns 11.0 – iagerogiannis Aug 14 '20 at 02:30
  • @franciscosollima: I also have problems with `proper_round(169.5)`. The first version yields `1610.0` and the second `26.0`... – Olivier Oct 16 '21 at 14:21
  • `proper_round(xx9.5)` where xx can be any number or numbers doesn't work. 19.5->11.0, 29.5->12.0, 39.5->13.0, 139.5->23.0. – John Jul 20 '22 at 17:55
  • @plowman - the round() function uses round-half-to-even (colloquially Banker's Rounding). From the docs - "rounding is done toward the even choice (so, for example, both round(0.5) and round(-0.5) are 0, and round(1.5) is 2)" https://docs.python.org/3/library/functions.html#round – Joe Carboni Aug 27 '22 at 19:02
29

Use round(x, y). It will round up your number up to your desired decimal place.

For example:

>>> round(32.268907563, 3)
32.269
milancurcic
  • 6,202
  • 2
  • 34
  • 47
Satyaki Sanyal
  • 1,201
  • 11
  • 12
  • 6
    Using `round(x)` will already return an integer in newer versions of python (`>=3.6`) – Stack Nov 14 '20 at 19:39
25

round(value,significantDigit) is the ordinary solution, however this does not operate as one would expect from a math perspective when round values ending in 5. If the 5 is in the digit just after the one you're rounded to, these values are only sometimes rounded up as expected (i.e. 8.005 rounding to two decimal digits gives 8.01). For certain values due to the quirks of floating point math, they are rounded down instead!

i.e.

>>> round(1.0005,3)
1.0
>>> round(2.0005,3)
2.001
>>> round(3.0005,3)
3.001
>>> round(4.0005,3)
4.0
>>> round(1.005,2)
1.0
>>> round(5.005,2)
5.0
>>> round(6.005,2)
6.0
>>> round(7.005,2)
7.0
>>> round(3.005,2)
3.0
>>> round(8.005,2)
8.01

Weird.

Assuming your intent is to do the traditional rounding for statistics in the sciences, this is a handy wrapper to get the round function working as expected needing to import extra stuff like Decimal.

>>> round(0.075,2)

0.07

>>> round(0.075+10**(-2*5),2)

0.08

Aha! So based on this we can make a function...

def roundTraditional(val,digits):
   return round(val+10**(-len(str(val))-1), digits)

Basically this adds a value guaranteed to be smaller than the least given digit of the string you're trying to use round on. By adding that small quantity it preserve's round's behavior in most cases, while now ensuring if the digit inferior to the one being rounded to is 5 it rounds up, and if it is 4 it rounds down.

The approach of using 10**(-len(val)-1) was deliberate, as it the largest small number you can add to force the shift, while also ensuring that the value you add never changes the rounding even if the decimal . is missing. I could use just 10**(-len(val)) with a condiditional if (val>1) to subtract 1 more... but it's simpler to just always subtract the 1 as that won't change much the applicable range of decimal numbers this workaround can properly handle. This approach will fail if your values reaches the limits of the type, this will fail, but for nearly the entire range of valid decimal values it should work.

You can also use the decimal library to accomplish this, but the wrapper I propose is simpler and may be preferred in some cases.


Edit: Thanks Blckknght for pointing out that the 5 fringe case occurs only for certain values. Also an earlier version of this answer wasn't explicit enough that the odd rounding behavior occurs only when the digit immediately inferior to the digit you're rounding to has a 5.

Jason R. Mick
  • 5,177
  • 4
  • 40
  • 69
  • I'm not sure why you think decimals with `5` as their last digit will always round down. That's not the case in a quick test I just did with numbers like `1.5`, `2.5`, `3.5` and so on and `1.05`, `1.15`, `1.25`, `1.35` rounding to one decimal place. The first set (exact halves rounding to small integers) always round to an even integer. The latter set do not round consistently, probably due to inexact binary representations of some of the values. The floats that have exact binary representations like `1.25` round to have an even least significant digit, but the others appear to round randomly. – Blckknght Jul 07 '16 at 07:54
  • Interesting... you're right. `round(4.0005,3)` gives `4.0` and `round(1.0005,3)` gives `1.0`, but `round(2.0005,3)` gives `2.001` and `round(3.0005,3)` gives `3.001`. But that's precisely why my proposed solution is necessary ... you don't know what to expect from the stock round, on this significant case! – Jason R. Mick Jul 10 '16 at 06:08
  • Thanks for this. Your function will come in handy when this problem comes up. – TMWP Apr 30 '17 at 20:34
  • 1
    Did you mean to have `, digits` at end of that return statement? No pun intended. (*mean* I mean) – user3342816 Mar 20 '18 at 22:43
  • Ah correct, indeed that should have been in there. Good catch... surprised nobody else had noticed! Will save those who utilize the solution some frustration. :-) – Jason R. Mick Mar 23 '18 at 19:06
  • round(2345, -2) --> 2300.0 ! – Saish Mar 28 '18 at 16:39
  • That's expected ... `round(x, 0)`: round to ones place ; `round(x, -1)` : round to tens place; `round(x, -2)`; round to hundreds place. Normal round function in your example produces the predictable result. From my crude testing, I don't see vanilla `round` producing the irregular rounding in values `>= 1`, although it might hit for certain values or for large values. Premise of method is to add a very small number (negation of the length of input string minus one is somewhat arbitrarily chosen) to ensure the decimal `0.#....#50` cases behave predictably when rounding to the 2nd LSD. – Jason R. Mick Jun 12 '18 at 22:40
  • The answer from stackoverflow.com/a/59774823/866333 nails it for me: `print(Decimal(2.5).quantize(0, ROUND_HALF_UP))`. The batteries included, `Decimal`, is well documented in answers to: https://stackoverflow.com/questions/10825926/python-3-x-rounding-behavior, eg stackoverflow.com/a/52607477/866333. – John Jul 20 '22 at 20:29
23

For positives, try

int(x + 0.5)

To make it work for negatives too, try

int(x + (0.5 if x > 0 else -0.5))

int() works like a floor function and hence you can exploit this property. This is definitely the fastest way.

Confuse
  • 5,646
  • 7
  • 36
  • 58
  • 4
    doesn't work for negatives `>>> x=-0.999` `>>> int(x), round(x), int(x+0.5)` `(0, -1.0, 0)` – user2907934 Nov 05 '16 at 20:57
  • 3
    If you care about corner cases don't use the "add 0.5 and floor" technique - there are some values that may not round the way you expect! See https://stackoverflow.com/a/47302585/2732969 for a C++ take and the https://stackoverflow.com/a/38744026/2732969 answer in this very question. – Anon Aug 25 '18 at 07:15
  • I needed a fast method, it didn't have to be accurate and wouldn't have many corner cases, and error in corner cases isn't important in my scenario. So this is definitely my go to for some special cases where speed is priority. Don't recommend for precision or accuracy. – AgentM Oct 21 '19 at 18:27
16

Your solution is calling round without specifying the second argument (number of decimal places)

>>> round(0.44)
0
>>> round(0.64)
1

which is a much better result than

>>> int(round(0.44, 2))
0
>>> int(round(0.64, 2))
0

From the Python documentation at https://docs.python.org/3/library/functions.html#round

round(number[, ndigits])

Return number rounded to ndigits precision after the decimal point. If ndigits is omitted or is None, it returns the nearest integer to its input.

Note

The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.

Jay
  • 165
  • 1
  • 8
12

Isn't just Python doing round half to even, as prescribed by IEEE 754?

Be careful redefining, or using "non-standard" rounding…

(See also https://stackoverflow.com/a/33019948/109839)

Community
  • 1
  • 1
  • 2
    This answer is a bit unclear. `Round half to even` is absolutely not prescribed by IEEE 754, but is instead only one of several rounding options [described by the standard](https://en.wikipedia.org/wiki/IEEE_754#Roundings_to_nearest). `Round to nearest, ties away from zero` (ie the behavior that most people expect) is also an option, and is the default in, for example, C/C++. – tel May 14 '19 at 05:20
  • I agree, the wording is quite confusing. What I meant that Python is rounding half to heven (see the table at the end of https://docs.python.org/3.7/library/stdtypes.html#numeric-types-int-float-complex where `round` is explained) and it is doing so according to the way "round half to even" is prescribed to work (or described) by the standard. –  May 15 '19 at 21:57
11

You can also use numpy assuming if you are using python3.x here is an example

import numpy as np
x = 2.3
print(np.rint(x))
>>> 2.0
sushmit
  • 4,369
  • 2
  • 35
  • 38
1

If you need (for example) a two digit approximation for A, then int(A*100+0.5)/100.0 will do what you are looking for.

If you need three digit approximation multiply and divide by 1000 and so on.

dav1d
  • 5,917
  • 1
  • 33
  • 52
Hoo
  • 11
  • 1
1

Some thing like this should also work

import numpy as np    

def proper_round(a):
    '''
    given any real number 'a' returns an integer closest to 'a'
    '''
    a_ceil = np.ceil(a)
    a_floor = np.floor(a)
    if np.abs(a_ceil - a) < np.abs(a_floor - a):
        return int(a_ceil)
    else:
        return int(a_floor)
Aabhaas
  • 11
  • 2
1

The rationale for rounding a x500... to the even value in the 'x' digit is the correct method and has nothing to do with the binary represntation of the value. It is the 'proper' algorithm to employ. Advanced science classes and statistics classes teach this. But why is this the case?

Minimizing accumulated errors!

When you round a value, you introduce an error value on that term. If you are summing together masses of values and you always round an x500... value up [i.e. to x+1], then you will have an inherent bias to your error accumulation.

On a set of N numbers, where M of those numbers are of the form x500..., a round up methodology will introduce a +ve error term of magnitude M * 0.5 of the digit you are rounding to.

However, if you always round to even, then you have an expectation that half of those M error terms are +ve and half are -ve. Therefore, you have the expecatation that they will cancel leaving you with a net zero error from those M terms. Isn't an error of 0 a whole lot better than an error of M * 0.5?

But, that net 0 error is only an expectation. When you consider flipping a "fair" coin (an analogous problem) such that "heads" = +1 and "tails" = -1, M times, and add up the sum of all flips, there is a statistical expectation of a 0 result, but the magnitude of the "wander" from 0 (could be +ve or -ve) is generally bound by sqrt(M) -- or is it log(M)? haven't cracked open a statistics book in quite some time. The actual answer (sqrt or log) is irrelevant to this question as both sqrt(M) and log(M) are smaller than M for all M>1].

So, as it pertains to our summation of numbers scenario, with M values of the form x500..., since M is finite, we have an expectation that the error term from those M values will be zero, but we also have an even stronger expectation that the magnitude of the error term ought to be bound by sqrt(M) * 0.5 of the digit you are rounding to.

0 is fantastic, but an expected sqrt(M) * 0.5 is still a whole lot better than guaranteed M * 0.5.

Yes, you can have a pathological case where you end up with M * 0.5, but such a scenario should be an outlier rather than the norm -- and that rare outlier is no worse than the "always round up" approach.

anon
  • 11
  • 1
1

In the correct answer mentioned in this does not work if I use 79.5. It gives an answer of 710, not 80.

def proper_round(num, dec=0):
num= int(num*pow(10,dec+1))
lastDigit = (str(num))[-1]
num = int(num/10)
if lastDigit >='5':
    num+=1
return float(num/pow(10,dec))

The above mentioned code gives better result

Sabeer
  • 19
  • 2
0

I use and may advise the following solution (python3.6):

y = int(x + (x % (1 if x >= 0 else -1)))

It works fine for half-numbers (positives and negatives) and works even faster than int(round(x)):

round_methods = [lambda x: int(round(x)), 
                 lambda x: int(x + (x % (1 if x >= 0 else -1))),
                 lambda x: np.rint(x).astype(int),
                 lambda x: int(proper_round(x))]

for rm in round_methods:
    %timeit rm(112.5)
Out:
201 ns ± 3.96 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
159 ns ± 0.646 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
925 ns ± 7.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
1.18 µs ± 8.66 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

for rm in round_methods:
    print(rm(112.4), rm(112.5), rm(112.6))
    print(rm(-12.4), rm(-12.5), rm(-12.6))
    print('=' * 11)

Out:
112 112 113
-12 -12 -13
===========
112 113 113
-12 -13 -13
===========
112 112 113
-12 -12 -13
===========
112 113 113
-12 -13 -13
===========
0

This one is tricky, to be honest. There are many simple ways to do this nevertheless. Using math.ceil(), round(), and math.floor(), you can get a integer by using for example:

n = int(round(n))

If before we used this function n = 5.23, we would get returned 5. If you wanted to round to different place values, you could use this function:

def Round(n,k):
  point = '%0.' + str(k) + 'f'
  if k == 0:
    return int(point % n)
  else:
    return float(point % n)

If we used n (5.23) again, round it to the nearest tenth, and print the answer to the console, our code would be:

Round(5.23,1)

Which would return 5.2. Finally, if you wanted to round something to the nearest, let's say, 1.2, you can use the code:

def Round(n,k):
    return k * round(n/k)

If we wanted n to be rounded to 1.2, our code would be:

print(Round(n,1.2))

and our result:

4.8

Thank you! If you have any questions, please add a comment. :) (Happy Holidays!)

0

Click the link for the explanation. The article linked in my post talks all about the code and how to use it.

https://kodify.net/python/math/truncate-decimals/

import math

def truncate(number, decimals=0):
    """
    Returns a value truncated to a specific number of decimal places.
    """
    if not isinstance(decimals, int):
        raise TypeError("decimal places must be an integer.")
    elif decimals < 0:
        raise ValueError("decimal places has to be 0 or more.")
    elif decimals == 0:
        return math.trunc(number)

    factor = 10.0 ** decimals
    return math.trunc(number * factor) / factor
  • While this code may provide a solution to problem, it is highly recommended that you provide additional context regarding why and/or how this code answers the question. Code only answers typically become useless in the long-run because future viewers experiencing similar problems cannot understand the reasoning behind the solution. – neuron Oct 10 '21 at 19:16
  • Click the link for the explanation. The article linked in my post talks all about it and how to use it. – Smoooth Soundz Oct 11 '21 at 16:37
0

for rounding numbers to the nearest int number, use numpy:

p=np.array([     430.15,      57.868,      70.697,      198.13])
p=np.round(p)

and the p :

array([        430,          58,          71,         198])
Ali Ganjbakhsh
  • 541
  • 7
  • 13
  • try with this data `p = np.array([4.4, 4.5, 4.6, 5.4, 5.5, 5.6])`, analyse the outcome and get surprised. you should have read other answers to this question, before posting yours. – Nik O'Lai May 11 '23 at 16:50
-1

For this purpose I would suggest just do the following thing -

int(round(x))

This will give you nearest integer.

Hope this helps!!