Python 为什么numpy.linspace中的值不如其他numpy.float64值精确?
我希望生成具有十进制间距的numpy数组,例如[0.05、0.10、0.15…]。该函数似乎合适,因为示例包括浮点数组。但是,我无法理解我得到的结果:Python 为什么numpy.linspace中的值不如其他numpy.float64值精确?,python,numpy,floating-point,Python,Numpy,Floating Point,我希望生成具有十进制间距的numpy数组,例如[0.05、0.10、0.15…]。该函数似乎合适,因为示例包括浮点数组。但是,我无法理解我得到的结果: import numpy as np x = np.linspace(2110.35, 2149.05, 775) print(x[-1], type(x[-1])) print(x[-3], type(x[-3])) 2149.05 <class 'numpy.float64'> 2148.950000000000
import numpy as np
x = np.linspace(2110.35, 2149.05, 775)
print(x[-1], type(x[-1]))
print(x[-3], type(x[-3]))
2149.05 <class 'numpy.float64'>
2148.9500000000003 <class 'numpy.float64'>
当我想使用比较运算符时,这会导致一个问题:
x[-3] <= y
False
x[-3]编辑2020-11-21:(从这里开始添加部分)
差异原因:
浏览一下下面的内容:
首先,根据给定的开始
、停止
和数值
计算步骤。只有一种方法可以做到这一点,而且计算步骤
的方式没有错误或不精确
连续的输出值是通过取开始
的基值,并向开始
值添加越来越高的步骤
倍数而产生的。因此,例如,第一个值是通过将(0*step)
添加到start
生成的,下一个值是通过将(1*step)
添加到start
生成的,依此类推。同样,这方面没有任何错误、不寻常或不精确之处。请记住,linspace()
的主要承诺是确保间隔的一致性,并确保输出所需的样本数
如果调用linspace()
时endpoint=True
,则最后一个输出值将强制等于指定的stop
值。同样,对于大多数用例来说,这似乎是更可取的做法,而不是输出一个计算值作为最后一个值,不管最后一个值计算得多么巧妙
那么,不精确性从何而来
我可以想到这里讨论的差异的三个原因:
对start
、stop
和num
的给定输入值的错误期望:即使在FP表示中没有固有限制的理想世界中,当start=2110.35
时,x[-3]
不可能等于2148.95
,stop=2149.05
,和num=775
。如果这些输入值稍有不同,例如num=776
而不是num=775
,则可能只,——在这种情况下(停止-启动)/(776-1)
将是0.05
的一个很好的圆间距,这意味着期望x[-3]
等于2148.95
start
和start
值本身无法在FP中精确表示。(请记住,大多数分数只能在某些不精确的情况下用FP表示。值得注意的例外是2
的负幂分数)。因此,通过启动
和停止
,我们看到的不是计算机看到的。这显然会影响步骤的计算。此计算的步骤
中的不精确性比(停止-启动)
中的不精确性小num
倍。但是,请注意,在输出序列快结束时,(停止-启动)
中的这种不精确性将产生越来越大的影响,因为越来越多的步骤的倍数被添加到开始
。下面是一个例子:
产出:
2110.349999999999909050529822707176208496
2149.050000000000181898940354585647583008
0.125
(OP已经意识到)每个计算的输出值(无论计算多么精确)可以或不可以在FP中精确表示。实际上,FP固有的不精确性将影响大多数生成的输出值
假设地说,numpy
开发人员可能插入了额外的干预逻辑——他们可能试图以某种方式找出哪些输出值可以精确地表示在FP中,对于这些输出位置,它们可以输出强制真实值,而不是计算值。当endpoint=True
时,当前正在对最后一个输出值执行这样的干预。然而,这种过度干预除了影响速度外,还将违反间隔规则性的主要承诺
另一个假设性的争论可能是,连续值是否可以通过累积加法而不是通过(start+i*step)
生成。也就是说,第二个值可以计算为(第一步+步骤)
,第三个值可以计算为(第二步+步骤)
,依此类推。我不知道为什么没有选择这样一种实现方式,但我怀疑这会在精确性方面给我们带来任何好处。我现在怀疑导致不精确的因素仍然适用(FP表示法在精确表示计算值方面的固有局限性,以及开始
、停止
和步骤
中的不精确性)。它也可能会更慢,因为每个值只能在计算前一个值之后计算
总而言之,而不是linspace
算法是次优的,真正的罪魁祸首是——错误的期望(或错误的输入),FP表示的固有限制影响开始
,停止
,以及计算值
编辑2020-11-21:(新增部分到此结束)
你的小测试:
print(y, type(y))
产生产出:
2148.95 <class 'numpy.float64'>
2148.95
不一定证明y
完全包含2148.95
这是因为,当您调用print(y)
时,它会在内部调用str(y)
,我们确实不太了解str(y)
生成其输出的精度
建议的行动
print(y, type(y))
2148.95 <class 'numpy.float64'>
x = np.linspace(2110.35, 2149.05, 775)
y = np.float64(2148.95)
print('y:')
print(format(y, '.20g'))
print('x[-3]:')
print(format(x[-3], '.20g'))
print('the next float after y:')
print(format(np.nextafter(y, y+1), '.20g'))
y:
2148.9499999999998181
x[-3]:
2148.9500000000002728
next float above y:
2148.9500000000002728
print('x[-3] rounded:')
print(format(round(x[-3], 2), '.20g'))
x[-3] rounded:
2148.9499999999998181