R 中的数值比较难度

Numeric comparison difficulty in R

提问人:Matt Parker 提问时间:5/5/2010 最后编辑:GSeeMatt Parker 更新时间:7/11/2023 访问量:38374

问:

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

浮点 比较运算符 R-FAQ

评论

2赞 Erdogan CEVHER 5/3/2019
在许多情况下,基于两个条件的解决方案是错误的,因此不能/不得使用: .因为:产生逻辑 OR 字符。因此,“合乎逻辑 |字符“类型不兼容。我将展示如何防止这种类型的不兼容。(a-b) > 0.5 | all.equal((a-b), 0.5)a <- 4.005; b <- 4.002; a-b > 0.5 | all.equal(a-b, 0.5) # Error in a - b > 0.5 | all.equal(a - b, 0.5) : operations are possible only for numeric, logical or complex typesall.equal
1赞 Erdogan CEVHER 5/20/2019
我在下面提出了一个非常简化的解决方案。

答:

15赞 Shane 5/5/2010 #1

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

# 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赞 Ken Williams 5/6/2010
我个人认为这是最好的方法,因为您不必自己决定 epsilon。你甚至可以从 Perl 中获取一个页面,并给它们起个名字,比如 、 和 。gelene
13赞 icio 5/5/2010 #2

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

round(0.58 - 0.08, 2) == 0.5

评论

5赞 Marek 5/5/2010
我认为这是最好的解决方案,对于我将使用的原始问题(10 位数字应该足以用于将来的扩展)。round(a-b, 10) >= 0.5
50赞 John 5/5/2010 #3

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

tol = 1e-5

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

一般来说,在没有四舍五入的情况下,只使用传统逻辑,我发现直线逻辑比所有逻辑都好。

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

abs(x-y) <= tol

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

评论

1赞 Josiah Yoder 8/4/2021
怎么了?它使用一个相当合理的默认值 -- eps 的平方根。是的,有时需要更大的公差,但您也可以指定这些公差。all.equal
0赞 Josiah Yoder 8/4/2021
也许是因为它还检查类型是否匹配?我发现自己使用 .isTRUE(all.equal(as.numeric(v2), as.numeric(v2)))
4赞 Rob Hyndman 5/5/2010 #4

选择一些公差级别:

epsilon <- 1e-10

然后使用

(a-b+epsilon) >= 0.5
3赞 Jer 5/6/2010 #5

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

这是真的 如果( (a-b) >= .5) 如果( (a-b) < .5)

其中之一应该始终对每对双打进行 True 评估。至少,任何使用一个代码都会隐式定义对另一个的 no 操作。如果你使用公差来获得实际的 .5 包含在第一个中,但你的问题是在连续域上定义的,那么你并没有完成太多。在大多数涉及潜在问题中连续值的问题中,这几乎没有意义,因为任意超过 .5 的值将始终按应有的方式计算。任意接近 .5 的值将进入“错误”的流量控制,但在使用适当精度的连续问题中,这无关紧要。

公差唯一有意义的时间是当您处理该类型的问题时 如果( (a-b) == c) if( (a-b) != c)

在这里,再多的“适当精度”也无济于事。原因是你必须做好准备,除非你手动将 a-b 的位设置为非常低的水平,否则第二个将始终计算为 true,而实际上你可能希望第一个有时是 true。

8赞 January 4/19/2017 #6

再说一句。是泛型的。对于数值,它使用 .对此函数的检查表明,它使用了 ,其中定义为all.equalall.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
2赞 Erdogan CEVHER 5/3/2019 #7

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

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
0赞 NicChr 7/11/2023 #8

如果它对任何人有帮助,我最近开始使用这些助手。

不确定它们是否完全正确,尽管很高兴这些可能不起作用的任何示例。

double_equal <- function(x, y, tol = sqrt(.Machine$double.eps)){
  abs(x - y) < tol
}
double_gt <- function(x, y, tol = sqrt(.Machine$double.eps)){
  (x - y) > tol
}
double_gte <- function(x, y, tol = sqrt(.Machine$double.eps)){
  (x - y) > -tol
}
double_lt <- function(x, y, tol = sqrt(.Machine$double.eps)){
  (x - y) < -tol
}
double_lte <- function(x, y, tol = sqrt(.Machine$double.eps)){
  (x - y) < tol
}
x <- sqrt(2)^2
y <- 2
double_gte(x, y)
#> [1] TRUE
double_gte(y, x)
#> [1] TRUE

double_gt(x, y)
#> [1] FALSE
double_gt(y, x)
#> [1] FALSE

double_lte(x, y)
#> [1] TRUE
double_lte(y, x)
#> [1] TRUE

double_lt(x, y)
#> [1] FALSE
double_lt(y, x)
#> [1] FALSE