使用numpy.rint()舍入到最接近的整数值与.5不一致

u1ehiz5o  于 2023-01-02  发布在  其他
关注(0)|答案(9)|浏览(145)

numpy's 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向下取整,这是有原因的吗?这是the inaccuracy of floats的公正和伪影吗?

    • 编辑**

是否有一种方法可以获得所需的功能,其中n.5被四舍五入,即对于n =偶数或奇数都被四舍五入为n +1?

fcy6dtqo

fcy6dtqo1#

所以这种行为(如注解中所述),是一种非常传统的舍入形式,常见于舍入一半到偶数的方法。也称为(根据大卫Heffernan的说法)作为银行家的舍入。关于这种行为的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函数(通常是ceiling函数)可以做到这一点;如果你想相反(总是向下取整),np.floor函数可以做到这一点。

wr98u20j

wr98u20j2#

事实上,这正是IEEE浮点标准IEEE 754指定的舍入(1985和2008)。它的目的是使舍入无偏。在正态概率理论中,两个整数之间的随机数恰好为N + 0.5的概率为零,因此如何舍入它并不重要,因为这种情况从未发生过。但在真实的程序中,数字不是随机的,N + 0.5经常出现。(事实上,每当浮点数失去1位精度时,你就必须四舍五入0.5!)如果你总是把0.5四舍五入到下一个最大的数字,那么一堆四舍五入数字的平均值可能会略大于未四舍五入数字的平均值:这种偏差或漂移可能对一些数值算法具有非常坏的影响,并使它们不准确。
四舍五入到偶数比四舍五入到奇数好的原因是最后一位数肯定是零,所以如果你必须除以2并再次四舍五入,你根本不会丢失任何信息。
总之,这种四舍五入是数学家们所能设计出的最好的方法,在大多数情况下你都应该想要它,现在我们所需要做的就是让学校开始教孩子们。

uhry853o

uhry853o3#

Numpy舍入确实会向偶数舍入,但其他舍入模式可以使用运算的组合来表示。

>>> 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),但预期达到它们的计算可能无法精确表示。例如,10的负幂不能精确表示:

>>> (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]
2nbm6dog

2nbm6dog4#

Numpy使用bankers舍入,因此0.5舍入到最接近的偶数。如果您总是想向上舍入0.5而向下舍入0.4:
第一个月
或者,如果您总是想向下舍入0.5,向下舍入0.4,向上舍入0.6:
np.rint(np.nextafter(a, a-1))
注意,如果你想要相同的逻辑但不想要整数,这也适用于np.around

>>> a = np.array([1, 1.5, 2, 2.5, 3, 3.5])
>>> np.rint(a)
array([1., 2., 2., 2., 3., 4.])
>>> np.rint(np.nextafter(a, a+1))
array([1., 2., 2., 3., 3., 4.])
>>> np.rint(np.nextafter(a, a-1))
array([1., 1., 2., 2., 3., 3.])

这是怎么回事?nextafter给出了一个方向上的下一个可表示数,因此这足以将该数推离“恰好”2.5。
请注意,这与ceilfloor不同。

>>> np.ceil(a)
array([1., 2., 2., 3., 3., 4.])
>>> np.floor(a)
array([1., 1., 2., 2., 3., 3.])
bq3bfh9z

bq3bfh9z5#

给你一个答案编辑:

y = int(np.floor(n + 0.5))
monwx1rj

monwx1rj6#

内置的round函数似乎可以满足您的需要,尽管它只适用于标量:

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]
ki1q1bka

ki1q1bka7#

不确定这是否是最有效的解决方案,但它确实有效:

signs = np.sign(arr)
tmp = signs * arr
arr = np.floor(tmp + 0.5)
arr = arr * signs
vfh0ocws

vfh0ocws8#

四舍五入函数用于标量、列表和numpy数组:

import numpy as np

def round_half_up(x):
    round_lambda = lambda z: (int(z > 0) - int(z < 0)) * int(abs(z) + 0.5)
    if isinstance(x, (np.ndarray, np.generic)):
        return np.vectorize(round_lambda)(x)
    else:
        return round_lambda(x)
8fq7wneg

8fq7wneg9#

这对我很有效:

def my_round(a):
    return np.round(a)*(a-np.floor(a)!=0.5) + np.ceil(a)*(a-np.floor(a)==0.5)

>>> my_round([0.5, 1.5, 2.5, 3.5])

array([1., 2., 3., 4.])

相关问题