Skip to content

Commit 897b915

Browse files
authored
Merge pull request satwikkansal#268 from LiquidFun/master
Add new snippet: banker's rounding
2 parents b89710a + 6642876 commit 897b915

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

CONTRIBUTORS.md

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Following are the wonderful people (in no specific order) who have contributed t
2424
| Jongy | [Jongy](https://github.com/Jongy) | [#208](https://github.com/satwikkansal/wtfpython/issues/208), [#210](https://github.com/satwikkansal/wtfpython/issues/210), [#233](https://github.com/satwikkansal/wtfpython/issues/233) |
2525
| Diptangsu Goswami | [diptangsu](https://github.com/diptangsu) | [#193](https://github.com/satwikkansal/wtfpython/issues/193) |
2626
| Charles | [charles-l](https://github.com/charles-l) | [#245](https://github.com/satwikkansal/wtfpython/issues/245)
27+
| LiquidFun | [LiquidFun](https://github.com/LiquidFun) | [#267](https://github.com/satwikkansal/wtfpython/issues/267)
2728

2829
---
2930

README.md

+53
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ So, here we go...
7070
+ [▶ Catching the Exceptions](#-catching-the-exceptions)
7171
+ [▶ Same operands, different story!](#-same-operands-different-story)
7272
+ [▶ Name resolution ignoring class scope](#-name-resolution-ignoring-class-scope)
73+
+ [▶ Rounding like a banker *](#-rounding-like-a-banker-)
7374
+ [▶ Needles in a Haystack *](#-needles-in-a-haystack-)
7475
+ [▶ Splitsies *](#-splitsies-)
7576
+ [▶ Wild imports *](#-wild-imports-)
@@ -2532,6 +2533,58 @@ class SomeClass:
25322533
25332534
---
25342535
2536+
### ▶ Rounding like a banker *
2537+
2538+
Let's implement a naive function to get the middle element of a list:
2539+
```py
2540+
def get_middle(some_list):
2541+
mid_index = round(len(some_list) / 2)
2542+
return some_list[mid_index - 1]
2543+
```
2544+
2545+
**Python 3.x:**
2546+
```py
2547+
>>> get_middle([1]) # looks good
2548+
1
2549+
>>> get_middle([1,2,3]) # looks good
2550+
2
2551+
>>> get_middle([1,2,3,4,5]) # huh?
2552+
2
2553+
>>> len([1,2,3,4,5]) / 2 # good
2554+
2.5
2555+
>>> round(len([1,2,3,4,5]) / 2) # why?
2556+
2
2557+
```
2558+
It seems as though Python rounded 2.5 to 2.
2559+
2560+
#### 💡 Explanation:
2561+
2562+
This is not a float precision error, in fact, this behavior is intentional. Since Python 3.0, `round()` uses [banker's rounding](https://en.wikipedia.org/wiki/Rounding#Round_half_to_even) where .5 fractions are rounded to the nearest **even** number:
2563+
2564+
```py
2565+
>>> round(0.5)
2566+
0
2567+
>>> round(1.5)
2568+
2
2569+
>>> round(2.5)
2570+
2
2571+
>>> import numpy # numpy does the same
2572+
>>> numpy.round(0.5)
2573+
0.0
2574+
>>> numpy.round(1.5)
2575+
2.0
2576+
>>> numpy.round(2.5)
2577+
2.0
2578+
```
2579+
2580+
This is the recommended way to round .5 fractions as described in [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules). However, the other way (round away from zero) is taught in school most of the time, so banker's rounding is likely not that well known. Furthermore, some of the most popular programming languages (for example: JavaScript, Java, C/C++, Ruby, Rust) do not use banker's rounding either. Therefore, this is still quite special to Python and may result in confusion when rounding fractions.
2581+
2582+
See the [round() docs](https://docs.python.org/3/library/functions.html#round) or [this stackoverflow thread](https://stackoverflow.com/questions/10825926/python-3-x-rounding-behavior) for more information.
2583+
2584+
Note that `get_middle([1])` only returned 1 because the index was `round(0.5) - 1 = 0 - 1 = -1`, returning the last element in the list.
2585+
2586+
---
2587+
25352588
### ▶ Needles in a Haystack *
25362589
25372590
<!-- Example ID: 52a199b1-989a-4b28-8910-dff562cebba9 --->

0 commit comments

Comments
 (0)