下面的代碼顯然是錯誤的。有什麼問題?這些數字爲什麼不相等?
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