R中的数字比较难度

时间:2010-05-04 22:55:49

标签: r floating-point comparison comparison-operators r-faq

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

(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中大于或等于比较的合理方法,因为>=不能真正被信任?

7 个答案:

答案 0 :(得分:36)

对于这些事情,我从未成为all.equal的粉丝。在我看来,宽容有时会以神秘的方式起作用。为什么不检查大于小于0.05的公差

tol = 1e-5

(a-b) >= (0.05-tol)

一般情况下,如果没有四舍五入,只使用传统逻辑,我发现直接逻辑比all.equal

更好

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

abs(x-y) <= tol

您必须为all.equal设置容差,这比all.equal更紧凑,更直接。

答案 1 :(得分:11)

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

# 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

答案 2 :(得分:6)

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

round(0.58 - 0.08, 2) == 0.5

答案 3 :(得分:3)

选择一些容忍度:

epsilon <- 1e-10

然后使用

(a-b+epsilon) >= 0.5

答案 4 :(得分:2)

但是,如果您仍然使用公差,为什么还要关心a-b == .5(实际上)是否未被评估?无论如何,如果你正在使用公差,你说我并不完全关心终点。

这是真的 if((a-b)&gt; = .5) if((a-b)&lt; .5)

其中一个应始终在每对双打中评估为真。任何使用一个代码的代码至少隐含地在另一个代码上定义no操作。如果您使用容差来获得实际.5包含在第一个,但您的问题是在连续域上定义的,那么您将无法完成任何工作。在涉及基础问题中持续价值的大多数问题中,没有什么意义,因为任意超过.5的值将始终按其应有的方式进行评估。任意接近.5的值将转到“错误的”流量控制,但是在使用适当精度无关紧要的连续问题中。

容差有意义的唯一时间是在处理类型问题时 if((a-b)== c) if((a-b)!= c)

这里没有“适当的精确度”可以帮助你。原因是你必须做好准备,第二个将永远评估为真,除非你手动将a-b的位设置在一个非常低的水平,而事实上你可能希望第一个有时是真的。

答案 5 :(得分:1)

还有一条评论。 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

答案 6 :(得分:1)

当浮点数的数字难度增加时,

<=>=比较不是特定于语言的。

IsSmallerOrEqual <- function(a,b) {   # To check a <= b
# Check whether "Mean relative difference..." exist in all.equal's result; 
# If exists, it results in character, not logical
if (   class(all.equal(a, b)) == "logical" && (a<b | all.equal(a, b))) { return(TRUE)
 } else if (a < b) { return(TRUE)
     } else { return(FALSE) }
}

IsSmallerOrEqual(abs(-2-(-2.2)), 0.2) # TRUE; To check |-2-(-2.2)| <= 0.2
IsSmallerOrEqual(abs(-2-(-2.2)), 0.3) # TRUE
IsSmallerOrEqual(abs(-2-(-2.2)), 0.1) # FALSE

IsBiggerOrEqual  <- function(a,b) {   # To check a >= b
# Check whether "Mean relative difference..." exist in all.equal's result; 
# If exists, it results in character, not logical
if (   class(all.equal(a, b)) == "logical" && (a>b | all.equal(a, b))) { return(TRUE)
 } else if (a > b) { return(TRUE)
     } else { return(FALSE) }
}
IsBiggerOrEqual(3,3) # TRUE
IsBiggerOrEqual(4,3) # TRUE
IsBiggerOrEqual(3,4) # FALSE
IsBiggerOrEqual(0.58 - 0.08,0.5)  # TRUE

如果未处理all.equal,我们可能会遇到错误。

以下内容不是必需的,而是有用的:

abs(-2-(-2.2)) # 0.2

sprintf("%.54f",abs(-2-(-2.2)))  # "0.200000000000000177635683940025046467781066894531250000"
sprintf("%.54f",0.2)             # "0.200000000000000011102230246251565404236316680908203125"

all.equal(abs(-2-(-2.2)), 0.2)  # TRUE; check nearly equivalence of floating point numbers
identical(abs(-2-(-2.2)), 0.2)  # FALSE; check exact equivalence of floating point numbers