0

According to Python's Banker's rounding way of rounding, it should round to an even number if it is .5 and would therefore go to 3.2. It is the same for 0.15, 1.15, 2.15 and 3.15 but 4.15 is as I would expect. Why does it do this, as banker's rounding (rounding to the even value) should mean it rounds to .2 instead of .1.

What happened:

>>> print(round(3.15, 1))
3.1

What I expected:

>>> print(round(3.15, 1))
3.2
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
  • This has some info: [Python 3.x rounding behavior](https://stackoverflow.com/questions/10825926/python-3-x-rounding-behavior) – Peter Apr 21 '23 at 10:59
  • 3
    The OP understands that the python `round` function uses banker's rounding, they are asking why *apparently* for some numbers it doesn't. – juanpa.arrivillaga Apr 21 '23 at 11:04

3 Answers3

3

This is due to floating point issues, None of the values represented by those float literals can be represented exactly in binary, so they are always slightly above or below what you wrote in the source code:

import decimal

floats = 0.15, 1.15, 2.15, 3.15, 4.15
for f in floats:
    print(decimal.Decimal(f))

You see:

0.1499999999999999944488848768742172978818416595458984375
1.149999999999999911182158029987476766109466552734375
2.149999999999999911182158029987476766109466552734375
3.149999999999999911182158029987476766109466552734375
4.1500000000000003552713678800500929355621337890625

Note, if we use the exact values:

decimals = list(map(decimal.Decimal, ['0.15', '1.15', '2.15', '3.15', '4.15']))
for d in decimals:
    print(d, round(d, 1))

We get what you would expect:

0.15 0.2
1.15 1.2
2.15 2.2
3.15 3.2
4.15 4.2

We can construct floats that we know are representable exactly in binary (they are powers of 2):

exact = [(n, 2**(-n)) for n in range(1, 6)]
for exponent, f in exact:
    print(decimal.Decimal(f), round(f, exponent-1))

And we see now the float objects "work":

0.5 0.0
0.25 0.2
0.125 0.12
0.0625 0.062
0.03125 0.0312
juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172
  • Amusingly, your last demo doesn't demonstrate banker's rounding. All those numbers get rounded *down*. – Kelly Bundy Apr 21 '23 at 11:24
  • @KellyBundy Good point. In each case, `1 - f` would be an exact binary fraction that gets rounded *up*. Maybe this could be added to the answer. – alani Apr 21 '23 at 11:32
  • @alani I might use [odd / 8](https://tio.run/##LYxBCsIwEEX3c4q/TCAo6iYU3HkLcRHapB1oJ2FIQU8fjfo3b/F4v7zqkuXii7bGW8laMcWRt7ASxWcYK664M47wSFnBYIEGmaM5OXiHs31QF6mLbzAQPivKUs3/6nD70STroHmXyaSeWmrtDQ). – Kelly Bundy Apr 21 '23 at 11:38
  • 1
    @KellyBundy well, they are supposed to be rounded down according to banker's rounding. – juanpa.arrivillaga Apr 21 '23 at 17:53
  • That makes sense, I didn’t realise Python couldn’t store decimals exactly, but I understand now. Thanks – Zach Tydeman Apr 22 '23 at 12:20
  • @ZachTydeman this isn't something that is an issue in *Python*, it is an issue with floating point numbers – juanpa.arrivillaga Apr 22 '23 at 17:34
0

Read the function's documentation.

enter image description here

Kelly Bundy
  • 23,480
  • 7
  • 29
  • 65
-2

I used this function found at https://realpython.com/python-rounding/ and it returns what I expect:

import math
def round_up(n, decimals=0):
    multiplier = 10 ** decimals
    return math.ceil(n * multiplier) / multiplier

and now:

floats = 0.15, 1.15, 2.15, 3.15, 4.15
for f in floats:
...round_up(f,1)

0.2
1.2
2.2
3.2
4.2
deceze
  • 510,633
  • 85
  • 743
  • 889
  • 2
    the OP doesn't want to round up, they want banker's rounding (to the nearest even last digit). So, `0.25` should round to `0.2` not to `0.3` – juanpa.arrivillaga Apr 21 '23 at 11:03