下面的代码显然是错误的。有什么问题?这些数字为什么不相等?
i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15
下面的代码显然是错误的。有什么问题?这些数字为什么不相等?
i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15
因为不是所有的数字可以准确地在IEEE floating point arithmetic来表示(即几乎所有的计算机用来表示十进制数和做数学与他们的标准),你不会总是得到你的预期。这是特别真实的,因为一些简单的有限小数(例如0.1和0.05)的值在计算机中没有精确地表示,所以对它们的算术结果可能不会给出与直接表示“已知“答案。
这是计算机算术的公知的限制和在几个地方讨论:
标准的解决方案,这在R
是不使用==
,而是all.equal
功能。或者说,因为all.equal
提供了有关差异的详细信息,如果有的话,isTRUE(all.equal(...))
。
if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")
产生
i equals 0.15
使用all.equal
代替==
的一些例子(最后一个示例是应该表明,这种将正确显示差异)。
0.1+0.05==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.05, 0.15))
#[1] TRUE
1-0.1-0.1-0.1==0.7
#[1] FALSE
isTRUE(all.equal(1-0.1-0.1-0.1, 0.7))
#[1] TRUE
0.3/0.1 == 3
#[1] FALSE
isTRUE(all.equal(0.3/0.1, 3))
#[1] TRUE
0.1+0.1==0.15
#[1] FALSE
isTRUE(all.equal(0.1+0.1, 0.15))
#[1] FALSE
一些细节,直接从answer to a similar question复制:
您遇到的问题是,浮点不能代表小数正是在大多数情况下,这意味着你会经常发现完全匹配失败。
,当你说,而[R略有在于:
1.1-0.2
#[1] 0.9
0.9
#[1] 0.9
你可以找出什么是真正想在十进制:
sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"
你可以看到这些数字是不同的,但表示是有点笨拙。如果我们以二进制看他们(当然,十六进制,相当于),我们得到一个更清晰的画面:
sprintf("%a",0.9)
#[1] "0x1.ccccccccccccdp-1"
sprintf("%a",1.1-0.2)
#[1] "0x1.ccccccccccccep-1"
sprintf("%a",1.1-0.2-0.9)
#[1] "0x1p-53"
你可以看到,他们通过2^-53
不同,这很重要,因为这个数字之间的最小可表示差两个数字的值接近1,就像这样。
我们可以找出任何给定的计算机此表示的最小的数是通过寻找R中的machine场的关系:
你可以利用这一点来创建一个“几乎等于”功能,将检查的区别接近于浮点中最小的可表示数字。实际上这已经存在:all.equal
。
?all.equal
#....
#all.equal(x,y) is a utility to compare R objects x and y testing ‘near equality’.
#....
#all.equal(target, current,
# tolerance = .Machine$double.eps^0.5,
# scale = NULL, check.attributes = TRUE, ...)
#....
所以all.equal功能实际上是检查该数字之间的差为2个尾数之间的最小差值的平方根。
这个算法在一个非常小的称为denormals的数字附近有点搞笑,但是您不必担心这个问题。
上面的讨论假定两个单值的比较。在R中,没有标量,只有向量和隐式向量化是语言的一个优势。为了比较矢量元素的价值,以前的原则成立,但实施略有不同。 ==
被矢量化(进行元素比较),而all.equal
将整个矢量作为单个实体进行比较。
使用前面的例子
a <- c(0.1+0.05, 1-0.1-0.1-0.1, 0.3/0.1, 0.1+0.1)
b <- c(0.15, 0.7, 3, 0.15)
==
不给“预期”的结果和all.equal
不进行逐元素
a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0."
isTRUE(all.equal(a,b))
#[1] FALSE
相反,它循环通过两个向量的一个版本必须可使用
mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1] TRUE TRUE TRUE FALSE
如果功能这个版本需要,可以将它写入
elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
可以称为只是
elementwise.all.equal(a, b)
#[1] TRUE TRUE TRUE FALSE
或者,而不是更加的函数调用包装all.equal
,你可以复制的相关内部all.equal.numeric
和使用隐式矢量化:
tolerance = .Machine$double.eps^0.5
# this is the default tolerance used in all.equal,
# but you can pick a different tolerance to match your needs
abs(a - b) < tolerance
#[1] TRUE TRUE TRUE FALSE
添加到布莱恩的评论(这是什么原因)你也能在由我们来这荷兰国际集团all.equal
代替:
# i <- 0.1
# i <- i + 0.05
# i
#if(all.equal(i, .15)) cat("i equals 0.15\n") else cat("i does not equal 0.15\n")
#i equals 0.15
这里每约书亚的警告是更新的代码(感谢Joshua):
i <- 0.1
i <- i + 0.05
i
if(isTRUE(all.equal(i, .15))) { #code was getting sloppy &went to multiple lines
cat("i equals 0.15\n")
} else {
cat("i does not equal 0.15\n")
}
#i equals 0.15
我错过了Brian的链接,这简洁地解释了我的回答。 – 2012-02-29 23:57:21
当有差异时'all.equal'不会返回'FALSE',所以当你在'if'语句中使用''时,你需要用'isTRUE'来包装它。 – 2012-03-01 00:49:18
这是hackish的,但快速:
if(round(i, 10)==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
参见HTTP: //stackoverflow.com/q/6874867和http://stackoverflow.com/q/2769510。 [R Inferno](http://www.burns-stat.com/pages/Tutor/R_inferno.pdf)也是一个很好的阅读。 – Aaron 2012-03-01 02:10:58