Python 为什么numpy.linspace中的值不如其他numpy.float64值精确?

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

我希望生成具有十进制间距的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.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