2

This question is about numpy.rint, which by definition rounds to the nearest integer. However, the following code produces inconsistent results.

In [1]: import numpy as np
        for i in range(5, 105, 10):
            print(i, np.rint(i/10))
Out[1]: 5 0 # Should be 1
        15 2 # Correct
        25 2 # Should be 3
        35 4 # Correct
        45 4 # Should be 5
        55 6 # Correct
        65 6 # Should be 7
        ...

So there appears to be a pattern: if, after division by 10, the units place is even, the number is rounded down, but if the units place is odd, the number is rounded up. However, according to rounding rules, the units place should not matter!

Either numpy should use the "round half up", that is, exactly at half, it rounds up to the next integer, or it should use the "round half down". It cannot do both, and be inconsistent.

Normally, I would open a bug report with numpy for this, but I am not sure if this is entirely numpy being weird, or some underlying quirk of how python interprets floating points, or due to loss of precision from conversion to binary and back.

Note that numpy.round(i, 0) also behaves the same.


A fix is to add a small fraction after the division by 10: numpy.rint(i/10 + 0.1), and the answers come correct.

Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
Kartik
  • 8,347
  • 39
  • 73

2 Answers2

5

Since the near-universal adoption of the IEEE 754 standard, numeric functions have flocked to "round half to even" rounding, a description of which can be found on the same Wikipedia page you linked to, but a bit down.

Loss of precision during base conversion here is not a problem, because conversion of integers to floats (C doubles) in this range is exact, and the quotients are exactly representable in binary floating point.

Adding 0.1 is not a sane workaround. For example, add 0.1 to 4.42 and you get 4.52, which rounds up to 5.0 instead of to the correct 4.0.

user2357112
  • 260,549
  • 28
  • 431
  • 505
Tim Peters
  • 67,464
  • 13
  • 126
  • 132
  • I know adding 0.1 is not a correct solution, but it works for my case. I should have just used round up instead... – Kartik Oct 22 '17 at 18:12
1

The post by Tim Peters correctly explains that what you are seeing is rounding towards the nearest even integer in Python 3.

If you want to have Python 3 return numbers rounded to the nearest integer instead of the nearest even integer use

 numpy.floor(np.float(x)+0.5)

instead of numpy.rint(x)

For example:

for i in range(5,105,10):
   print(i,np.floor(np.float(i)/10+0.5))

prints

5 1.0
15 2.0
25 3.0
35 4.0
 ......
75 8.0
85 9.0
95 10.0
Dima Chubarov
  • 16,199
  • 6
  • 40
  • 76
  • First, that rounds `0.6` to `2.0`. Second, for a number halfway between two integers, neither choice is nearer. – user2357112 Oct 22 '17 at 23:36
  • @user2357112 Thanks for pointing that out. I was in a hurry to get the correct result and as the saying g oes "made an even number of mistakes that cancel each other out": 1) forgot to switch to floating point, 2) mixed up `floor` and `ceil`. – Dima Chubarov Oct 23 '17 at 02:57