R中的数值比较困难

R中的数值比较困难,r,floating-point,comparison,comparison-operators,r-faq,R,Floating Point,Comparison,Comparison Operators,R Faq,作为if语句条件的一部分,我尝试比较R中的两个数字: (a-b)>=0.5 在这个特殊的例子中,a=0.58和b=0.08。。。然而,(a-b)>=0.5是错误的。我意识到使用==进行精确数字比较的危险,这似乎是相关的: (a-b)==0.5)为false,而 all.equal((a-b),0.5)为真 我能想到的唯一解决办法是有两个条件:(a-b)>0.5 | all.equal((a-b),0.5)。这是可行的,但这真的是唯一的解决方案吗?我是否应该永远放弃比较运算符的=家族 为清晰起见进

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

(a-b)>=0.5

在这个特殊的例子中,a=0.58和b=0.08。。。然而,
(a-b)>=0.5
是错误的。我意识到使用
==
进行精确数字比较的危险,这似乎是相关的:

(a-b)==0.5)
为false,而

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
#使用公差
ε=%%`y)
#作为原始方法的新操作符
`%>=%`y)
#覆盖R的版本(不建议)
`>=‘y’
>(a-b)>=0.5
[1] 真的
>c(1,3,5)>=2:4
[1] 假假假真

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


对于这样的事情,我从来都不是所有人的粉丝。在我看来,宽容有时以神秘的方式发挥作用。为什么不检查大于小于0.05的公差

tol = 1e-5

(a-b) >= (0.05-tol)
总的来说,没有舍入,只有常规逻辑,我发现直接逻辑比所有逻辑都好

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

abs(x-y) <= tol

abs(x-y)选择一些公差级别:

epsilon <- 1e-10

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

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

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

只有在处理此类问题时,公差才有意义 如果((a-b)=c) 如果((a-b)!=c)


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

还有一条评论。
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
myeq
=
当浮点数的数字难度增加时,比较不是特定于语言的

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

我认为这是最好的解决方案,对于原始问题,我将使用
round(a-b,10)>=0.5
(10位数字应该足够用于将来的扩展)。我个人认为这是最好的方法,因为你不必自己决定ε。你甚至可以从Perl中取一页,给他们起个名字,比如
ge
le
ne
。你的解决方案基于两个条件
(a-b)>0.5 | all.equal((a-b),0.5)
在很多情况下是错误的,因此不能使用:
a 0.5 | all.equal(a-b,0.5):操作只能用于数字,逻辑或复杂类型
。因为:
all.equal
生成逻辑或字符。因此,出现了“逻辑|字符”类型不兼容。我将展示如何防止这种类型的不兼容。
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
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