51

我正在尝试将 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 中大于或等于比较的明智方法,因为>=不能真正信任?

4

7 回答 7

47

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

tol = 1e-5

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

一般来说,没有四舍五入和只使用常规逻辑,我发现直接逻辑比 all.equal 更好

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

abs(x-y) <= tol

无论如何,您都必须设置容差all.equal,这比all.equal.

于 2010-05-05T00:25:11.697 回答
14

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

# 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
于 2010-05-04T23:21:43.803 回答
12

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

round(0.58 - 0.08, 2) == 0.5
于 2010-05-05T00:12:42.557 回答
7

再来一条评论。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
于 2017-04-19T07:44:17.297 回答
4

选择一些容差水平:

epsilon <- 1e-10

然后使用

(a-b+epsilon) >= 0.5
于 2010-05-05T00:25:49.543 回答
3

但是,如果您仍然使用公差,为什么您关心 ab == .5 (实际上)没有得到评估?如果您无论如何都在使用公差,那么您是在说我并不完全关心端点。

以下是正确的 if( (ab) >= .5) if( (ab) < .5)

其中一个应该总是在每对双打上评估为真。至少,任何使用其中一个的代码都隐含地定义了对另一个的 no 操作。如果您使用公差来获得第一个包含的实际 0.5,但您的问题是在连续域上定义的,那么您并没有完成太多。在大多数涉及基础问题中的连续值的问题中,这一点几乎没有什么意义,因为任意超过 0.5 的值总是会按应有的方式进行评估。任意接近 0.5 的值将进入“错误”的流量控制,但在连续问题中,您使用适当的精度并不重要。

只有当您处理 if( (ab) == c) if( (ab) != c) 类型的问题时,公差才有意义

在这里,没有任何“适当的精度”可以帮助您。原因是您必须准备好第二个总是评估为真,除非您手动将 ab 的位设置为非常低的级别,而实际上您可能希望第一个有时为真。

于 2010-05-05T21:39:33.120 回答
2

<=>=当浮点数中出现数字难度时,比较不是特定于语言的。

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
于 2019-05-02T22:14:27.653 回答