【问题标题】:How to prevent float imprecision from affecting numpy.arange?如何防止浮点不精确影响numpy.arange?
【发布时间】:2018-07-23 04:01:23
【问题描述】:

因为numpy.arange() 使用ceil((stop - start)/step) 来确定项目的数量,所以一个小的浮点不精确(stop = .400000001) 可能会在列表中添加一个意想不到的值。

示例

第一种情况不包括停止点(有意)

>>> print(np.arange(.1,.3,.1))
[0.1 0.2]

第二种情况包括停止点(不打算)

>>> print(np.arange(.1,.4,.1))
[0.1 0.2 0.3 0.4]

numpy.linspace() 修复了这个问题,np.linspace(.1,.4-.1,3)。但要求您知道步数。 np.linspace(start,stop-step,np.ceil((stop-step)/step)) 导致同样的不一致。

问题

如何在不知道范围内元素数量的情况下生成可靠的float 范围?

极端情况

考虑我想生成一个未知精度的浮点索引的情况

np.arange(2.00(...)001,2.00(...)021,.00(...)001)

【问题讨论】:

  • 你保证 (stop-start) 是 step 的倍数吗?
  • 我读过的所有内容似乎都表明唯一可行的方法是从端点print(np.arange(.1,(.4 - step/1000),step)) 中减去步长的一部分。我不确定您是否可以避免这种情况,但最终也会失败
  • @user545424 你保证它是步长的倍数
  • @roganjosh 谢谢,这很有帮助。它类似于通过乘数转换int,因为您必须估计满足(1/step_size) < m < 1/precision_error的乘数m
  • 你从哪里得到这个步长值?你当初为什么会遇到这种情况?您可能最终会猜测公差。

标签: python python-2.7 numpy floating-point


【解决方案1】:

您的目标是计算 ceil((stop - start)/step) 的值,如果这些值是用精确的数学计算出来的。

这是不可能的,因为 只有 浮点值 startstopstep 是可能发生某些舍入错误的运算结果。四舍五入会删除信息,根本无法从缺乏信息中创建信息。

因此,只有在您有关于startstopstep 的其他信息时,才能解决此问题。

假设step 是精确的,但startstop 有一些累积的错误,它们以e0e1 为界。也就是说,您知道start 最多是e0 远离其理想数学值(在任一方向上),而stop 最多是e1 远离其理想值(在任一方向上)。那么(stop-start)/step 的理想值可能会在(stop-start-e0-e1)/step(stop-start+e0+e1)/step 之间偏离其理想值。

假设(stop-start-e0-e1)/step(stop-start+e0+e1)/step 之间有一个整数。那么仅仅从startstopstep的浮点值以及e0和@987654347的边界就不可能知道理想的ceil结果应该是较小的整数还是较大的整数@。

但是,根据您给出的示例,理想的(stop-start)/step 可以是一个整数,如(.4-.1)/.1。如果是这样,任何非零误差界限都可能导致误差区间跨越一个整数,从而使问题无法从我们目前掌握的信息中解决。

因此,为了解决问题,您必须掌握更多信息,而不仅仅是简单的错误界限。例如,您必须知道(stop-start)/step 恰好是一个整数或被量化。例如,如果您知道步数的理想计算会产生 0.1 的倍数,例如 3.8、3.9、4.0、4.1 或 4.2,但绝不会是 4.05,并且误差足够小,以至于浮动-点计算 (stop-start)/step 的最终误差小于 0.05,则可以将 (stop-start)/step 舍入到最接近的合格倍数,然后对其应用 ceil

如果你有这样的信息,你可以用你对startstopstep中错误的了解来更新问题(eg,也许它们每个都是从十进制到浮点的单次转换的结果)和理想(stop-start)/step 的可能值。如果您没有此类信息,则无法解决。

【讨论】:

    【解决方案2】:

    如果你保证(stop-start)step的倍数,那么你可以使用decimal模块来计算步数,即

    from decimal import Decimal
    
    def arange(start, stop, step):
        steps = (Decimal(stop) - Decimal(start))/Decimal(step)
    
        if steps % 1 != 0:
            raise ValueError("step is not a multiple of stop-start")
    
        return np.linspace(float(start),float(stop),int(steps),endpoint=False)
    
    print(arange('0.1','0.4','0.1'))
    

    【讨论】:

    • 除非步骤是1./3. 或其他不能用十进制表示的东西。
    【解决方案3】:

    如果你有你的目标和步骤的准确表示,如果它们是合理的,你可以使用fractions 模块:

    >>> from fractions import Fraction
    >>>
    >>> a = Fraction('1.0000000100000000042')
    >>> b = Fraction('1.0000002100000000002')
    >>> c = Fraction('0.0000000099999999998') * 5 / 3
    >>> 
    >>> float(a) + float(c) * np.arange(int((b-a)/c))
    array([1.00000001, 1.00000003, 1.00000004, 1.00000006, 1.00000008,
           1.00000009, 1.00000011, 1.00000013, 1.00000014, 1.00000016,
           1.00000018, 1.00000019])
    >>>
    >>> eps = Fraction(1, 10**100)
    >>> b2 = b - eps
    >>> float(a) + float(c) * np.arange(int((b2-a)/c))
    array([1.00000001, 1.00000003, 1.00000004, 1.00000006, 1.00000008,
           1.00000009, 1.00000011, 1.00000013, 1.00000014, 1.00000016,
           1.00000018])
    

    如果不是,您将不得不接受某种形式的截止:

    >>> a = 1.0
    >>> b = 1.003999999
    >>> c = 0.001
    >>> 
    # cut off at 4 decimals
    >>> round(float((b-a)/c), 4)
    4.0
    # cut off at 6 decimals
    >>> round(float((b-a)/c), 6)
    3.999999
    

    【讨论】:

      【解决方案4】:

      您可以在 Python 中使用format 函数将数字四舍五入到任意精度。

      比如要e小数点后的前三位,可以运行

      float(format(np.e, '.3f'))
      

      使用它来消除浮动不精确,你应该去去。

      【讨论】:

      • print(np.arange(.1,float(format(.4, '.1f')),.1)) print(np.arange(.1,float(format(.3, '.1f')),.1)) 产生与原始相同的不一致
      • 在对 OP 的示例进行测试时,这对我来说没有任何改变。这只是视觉效果
      猜你喜欢
      • 2021-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多