代码之家  ›  专栏  ›  技术社区  ›  Matt Parker

r中的数值比较困难

  •  35
  • Matt Parker  · 技术社区  · 15 年前

    作为if语句条件的一部分,我试图比较r中的两个数字:

    (a-b) >= 0.5

    在这个特殊的例子中,a=0.58,b=0.08…但是 (a-b)>=0.5 是假的。我知道使用 == 对于精确的数字比较,这似乎是相关的:

    (a - b) == 0.5) 是假的,而

    all.equal((a - b), 0.5) 是真的。

    我能想到的唯一解决办法是有两个条件: (a-b) > 0.5 | all.equal((a-b), 0.5) . 这是可行的,但这真的是唯一的解决办法吗?我应该发誓 = 一家永远的比较经营者?

    为了清晰起见,请编辑: 我知道这是个浮点问题。更根本的是,我要问的是:我该怎么办?在r中处理大于或等于比较的合理方法是什么,因为 >= 真的不能信任吗?

    6 回复  |  直到 7 年前
        1
  •  35
  •   John    10 年前

    我从来都不喜欢 all.equal 为了这样的事情。在我看来,宽容有时以神秘的方式起作用。为什么不检查一下大于0.05的公差

    tol = 1e-5
    
    (a-b) >= (0.05-tol)
    

    一般来说,如果没有舍入和常规逻辑,我发现直接逻辑比所有的逻辑都好。

    如果 x == y 然后 x-y == 0 . 也许 x-y 不完全是0,所以对于这种情况,我使用

    abs(x-y) <= tol
    

    不管怎样,你必须为 一律平等 这比 一律平等

        2
  •  11
  •   Shane    15 年前

    如果要经常使用此方法,可以将其创建为单独的运算符或覆盖原始的>=函数(可能不是个好主意):

    # using a tolerance
    epsilon <- 1e-10 # set this as a global setting
    `%>=%` <- function(x, y) (x + epsilon > y)
    
    # as a new operator with the original approach
    `%>=%` <- function(x, y) (all.equal(x, y)==TRUE | (x > y))
    
    # overwriting R's version (not advised)
    `>=` <- function(x, y) (isTRUE(all.equal(x, y)) | (x > y))
    
    > (a-b) >= 0.5
    [1] TRUE
    > c(1,3,5) >= 2:4
    [1] FALSE FALSE  TRUE
    
        3
  •  6
  •   icio    15 年前

    为了完整起见,我将指出,在某些情况下,您可以简单地四舍五入到小数点后几位(与之前发布的更好的解决方案相比,这是一种蹩脚的解决方案)。

    round(0.58 - 0.08, 2) == 0.5
    
        4
  •  3
  •   Rob Hyndman    15 年前

    选择一些公差等级:

    epsilon <- 1e-10
    

    然后使用

    (a-b+epsilon) >= 0.5
    
        5
  •  2
  •   Jer    15 年前

    但是,如果你仍然使用公差,为什么你关心a-b=.5(事实上)没有得到评估?如果你用的是公差,你是说我不在乎终点。

    这是事实 如果((a-b)>=0.5) 如果((a-b)<.5)

    其中一个应该总是对每对双打都正确。任何使用其中一个的代码都会隐式地定义另一个的no操作,至少是这样。如果你使用公差得到实际的.5包含在第一个,但你的问题是定义在一个连续的领域你学不到什么成就。在大多数涉及底层问题中的连续值的问题中,这一点几乎没有意义,因为任意超过.5的值将始终按其应有的方式计算。任意接近0.5的值将进入“错误”的流控制,但在连续的问题中,使用适当的精度并不重要。

    公差唯一有意义的时候是在处理类型的问题时 如果((a-b)==c) 如果((a-b)!= C)

    这里再多的“适当的精确”也帮不了你。原因是你必须准备好,除非你用手把a-b的位设置在一个很低的水平,否则第二个总是求值为真,而实际上你可能希望第一个有时是真的。

        6
  •  1
  •   January    8 年前

    再来一个评论。这个 all.equal 是通用的。对于数值,它使用 all.equal.numeric . 对这个函数的检查表明它使用了 .Machine$double.eps^0.5 ,其中 .Machine$double.eps 定义为

    double.eps: the smallest positive floating-point number ‘x’ such that
              ‘1 + x != 1’.  It equals ‘double.base ^ ulp.digits’ if either
              ‘double.base’ is 2 or ‘double.rounding’ is 0; otherwise, it
              is ‘(double.base ^ double.ulp.digits) / 2’.  Normally
              ‘2.220446e-16’.
    

    (.机器手册页)。

    换言之,对于你的宽容,这是一个可以接受的选择:

    myeq <- function(a, b, tol=.Machine$double.eps^0.5)
          abs(a - b) <= tol