提问人:Matt Parker 提问时间:5/5/2010 最后编辑:GSeeMatt Parker 更新时间:7/11/2023 访问量:38374
R 中的数值比较难度
Numeric comparison difficulty in R
问:
我正在尝试比较 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 中处理大于或等于比较的明智方法是什么,因为 不能真正可信?>=
答:
如果你想经常使用这种方法,你可以将其创建为一个单独的运算符,或者覆盖原始的 >= 函数(可能不是一个好主意):
# 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
评论
ge
le
ne
为了完整起见,我要指出的是,在某些情况下,您可以简单地四舍五入到小数点后几位(与之前发布的更好的解决方案相比,这是一个蹩脚的解决方案。
round(0.58 - 0.08, 2) == 0.5
评论
round(a-b, 10) >= 0.5
我从来都不喜欢这样的事情。在我看来,宽容有时以神秘的方式起作用。为什么不检查大于小于 0.05 的容差all.equal
tol = 1e-5
(a-b) >= (0.05-tol)
一般来说,在没有四舍五入的情况下,只使用传统逻辑,我发现直线逻辑比所有逻辑都好。
如果那么.也许不完全是 0,所以对于这种情况,我使用x == y
x-y == 0
x-y
abs(x-y) <= tol
无论如何,您都必须为 设置公差,这比 .all.equal
all.equal
评论
isTRUE(all.equal(as.numeric(v2), as.numeric(v2)))
选择一些公差级别:
epsilon <- 1e-10
然后使用
(a-b+epsilon) >= 0.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。
再说一句。是泛型的。对于数值,它使用 .对此函数的检查表明,它使用了 ,其中定义为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
<=
当浮点数中出现数值难度时,比较不是特定于语言的。>=
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
如果它对任何人有帮助,我最近开始使用这些助手。
不确定它们是否完全正确,尽管很高兴这些可能不起作用的任何示例。
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
评论
(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 types
all.equal