Округление до ближайшего int с помощью numpy.rint() не соответствует для .5

numpy round int, похоже, не согласуется с тем, как он связан с xxx.5

In [2]: np.rint(1.5)
Out[2]: 2.0

In [3]: np.rint(10.5)
Out[3]: 10.0

1.5 округляется, а 10.5 округляется вниз. Для этого есть причина? Это просто и артефакт неточности поплавков?

Edit

Есть ли способ получить желаемую функциональность, где n.5 округляется, т.е. до n + 1 для n = четного или нечетного?

Ответ 1

Итак, такое поведение (как отмечено в комментариях), является очень традиционной формой округления, видимой в круглой половине до четности метод. Также известен (по словам Дэвида Хеффернана) как округление банкира. Документация numpy вокруг этого поведения подразумевает, что они используют этот тип округления, но также подразумевает, что могут возникнуть проблемы с тем, как numpy взаимодействует с форматом с плавающей точкой IEEE. (показано ниже)

Notes
-----
For values exactly halfway between rounded decimal values, Numpy
rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0,
-0.5 and 0.5 round to 0.0, etc. Results may also be surprising due
to the inexact representation of decimal fractions in the IEEE
floating point standard [1]_ and errors introduced when scaling
by powers of ten.

Независимо от того, действительно ли это так, я честно не знаю. Я знаю, что большие части ядра numpy все еще записаны в FORTRAN 77, который предшествует стандарту IEEE (установлен в 1984 году), но я не знаю достаточно FORTRAN 77, чтобы сказать, есть ли какая-то проблема с интерфейсом здесь.

Если вы хотите просто округлить независимо, функция np.ceil (функция потолка в целом) сделает это. Если вы ищете противоположное (всегда округление), функция np.floor достигнет этого.

Ответ 2

Это на самом деле точно округление, указанное стандартом IEEE с плавающей запятой IEEE 754 (1985 и 2008). Он предназначен для округления объектива. В обычной теории вероятности случайное число между двумя целыми числами имеет нулевую вероятность быть ровно N + 0,5, поэтому не имеет значения, как вы его обходите, потому что этого никогда не происходит. Но в реальных программах цифры не являются случайными, а N + 0,5 встречается довольно часто. (Фактически, вы должны округлять 0,5 каждый раз, когда число с плавающей запятой теряет 1 бит точности!) Если вы всегда округляете 0,5 до следующего наибольшего числа, тогда среднее число округленных чисел в скобках, вероятно, будет немного больше, чем среднее значение безразмерных чисел: это смещение или дрейф может иметь очень плохое влияние на некоторые числовые алгоритмы и сделать их неточными.

Причина округления до четного лучше округления до нечетного - это то, что последняя цифра будет равна нулю, поэтому, если вам нужно разделить на 2 и снова округлить, вы не потеряете никакой информации.

Таким образом, такое округление - это лучшее, что математики смогли разработать, и вы должны ХОТИТЕ его в большинстве случаев. Теперь все, что нам нужно сделать, это заставить школы начать преподавать их детям.

Ответ 3

округление округлой формы округляется до четного, но другие режимы округления могут быть выражены с помощью комбинации операций.

>>> a=np.arange(-4,5)*0.5
>>> a
array([-2. , -1.5, -1. , -0.5,  0. ,  0.5,  1. ,  1.5,  2. ])
>>> np.floor(a)      # Towards -inf
array([-2., -2., -1., -1.,  0.,  0.,  1.,  1.,  2.])
>>> np.ceil(a)       # Towards +inf
array([-2., -1., -1., -0.,  0.,  1.,  1.,  2.,  2.])
>>> np.trunc(a)      # Towards 0
array([-2., -1., -1., -0.,  0.,  0.,  1.,  1.,  2.])
>>> a+np.copysign(0.5,a)   # Shift away from 0
array([-2.5, -2. , -1.5, -1. ,  0.5,  1. ,  1.5,  2. ,  2.5])
>>> np.trunc(a+np.copysign(0.5,a))   # 0.5 towards higher magnitude round
array([-2., -2., -1., -1.,  0.,  1.,  1.,  2.,  2.])

В общем случае номера формы n.5 могут быть точно представлены двоичной плавающей точкой (они равны m.1 в двоичном формате, как 0.5 = 2 ** - 1), но расчеты, ожидаемые для их достижения, могут не совпадать. Например, отрицательные силы десяти не точно представлены:

>>> (0.1).as_integer_ratio()
(3602879701896397, 36028797018963968)
>>> [10**n * 10**-n for n in range(20)]
[1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
 0.9999999999999999, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]

Ответ 4

Встроенная функция раунда, похоже, делает то, что вы хотите, хотя она работает только на скалярах:

def correct_round(x):
    try:
        y = [ round(z) for z in x ]
    except:
        y = round(x)    
    return y

а затем проверить:

print correct_round([-2.5,-1.5,-0.5,0.5,1.5,2.5])
> [-3.0, -2.0, -1.0, 1.0, 2.0, 3.0]