Aşağıdaki kod açıkça yanlıştır. Sorun ne?
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
Aşağıdaki kod açıkça yanlıştır. Sorun ne?
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
Yanıtlar:
Tüm sayılar tam olarak IEEE kayan nokta aritmetiğinde temsil edilemediğinden (neredeyse tüm bilgisayarların ondalık sayıları temsil etmek ve onlarla matematik yapmak için kullandığı standart), her zaman beklediğiniz şeyi elde edemezsiniz. Bu özellikle doğrudur, çünkü basit, sonlu ondalık sayılar (0.1 ve 0.05 gibi) tam olarak bilgisayarda temsil edilmez ve bu nedenle üzerlerindeki aritmetik sonuçlar, " bilinen cevap.
Bu, bilgisayar aritmetiğinin iyi bilinen bir sınırlamasıdır ve birkaç yerde tartışılmaktadır:
Bunun standart çözümü R
kullanmak ==
değil, all.equal
işlevdir. Ya da daha doğrusu, all.equal
eğer varsa, farklılıklar hakkında çok fazla ayrıntı verdiğinden isTRUE(all.equal(...))
,.
if(isTRUE(all.equal(i,0.15))) cat("i equals 0.15") else cat("i does not equal 0.15")
verim
i equals 0.15
all.equal
Bunun yerine kullanmaya ilişkin bazı örnekler ==
(son örnekte bunun farklılıklar doğru gösterileceği gösterilmektedir).
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
Benzer bir sorunun cevabından doğrudan kopyalanan biraz daha detay :
Karşılaştığınız sorun, kayan noktanın ondalık kesirleri çoğu durumda tam olarak temsil edememesidir, yani tam eşleşmelerin başarısız olduğunu sık sık bulacaksınız.
R dediğinde R hafifçe yatar:
1.1-0.2
#[1] 0.9
0.9
#[1] 0.9
Ondalık olarak gerçekten ne düşündüğünü öğrenebilirsiniz:
sprintf("%.54f",1.1-0.2)
#[1] "0.900000000000000133226762955018784850835800170898437500"
sprintf("%.54f",0.9)
#[1] "0.900000000000000022204460492503130808472633361816406250"
Bu sayıların farklı olduğunu görebilirsiniz, ancak gösterim biraz hantal. Onlara ikili olarak bakarsak (iyi, onaltılık, eşdeğer) daha net bir resim elde ederiz:
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
Farklı olduklarını görebilirsiniz , bu önemlidir çünkü bu sayı, değeri 1'e yakın olan iki sayı arasındaki en küçük temsil edilebilir farktır.
Herhangi bir bilgisayar için, R'nin makine alanına bakarak bu en küçük temsil edilebilir numaranın ne olduğunu bulabiliriz :
?.Machine
#....
#double.eps the smallest positive floating-point number x
#such that 1 + x != 1. It equals base^ulp.digits if either
#base is 2 or rounding is 0; otherwise, it is
#(base^ulp.digits) / 2. Normally 2.220446e-16.
#....
.Machine$double.eps
#[1] 2.220446e-16
sprintf("%a",.Machine$double.eps)
#[1] "0x1p-52"
Bu gerçeği, farkın kayan noktadaki en küçük temsil edilebilir sayıya yakın olduğunu kontrol eden 'neredeyse eşittir' işlevi oluşturmak için kullanabilirsiniz. Aslında bu zaten var: 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 işlevi aslında sayılar arasındaki farkın iki mantis arasındaki en küçük farkın kare kökü olduğunu kontrol etmektir.
Bu algoritma denormals denilen son derece küçük sayıların yakınında biraz komik oluyor, ancak bunun için endişelenmenize gerek yok.
Yukarıdaki tartışma iki tek değerin karşılaştırılmasını varsaymıştır. R'de skaler yoktur, sadece vektörler ve örtük vektörleşme dilin gücüdür. Vektörlerin değerini element açısından karşılaştırmak için önceki ilkeler geçerlidir, ancak uygulama biraz farklıdır. tüm vektörleri tek bir varlık olarak karşılaştırırken ==
, vektörize edilir (eleman bazında bir karşılaştırma yapar) all.equal
.
Önceki örnekleri kullanma
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)
==
"beklenen" sonucu all.equal
vermez ve öğe bazında çalışmaz
a==b
#[1] FALSE FALSE FALSE FALSE
all.equal(a,b)
#[1] "Mean relative difference: 0.01234568"
isTRUE(all.equal(a,b))
#[1] FALSE
Aksine, iki vektörün üzerinden geçen bir versiyon kullanılmalıdır.
mapply(function(x, y) {isTRUE(all.equal(x, y))}, a, b)
#[1] TRUE TRUE TRUE FALSE
Bunun işlevsel bir versiyonu isteniyorsa, yazılabilir
elementwise.all.equal <- Vectorize(function(x, y) {isTRUE(all.equal(x, y))})
ki bu sadece
elementwise.all.equal(a, b)
#[1] TRUE TRUE TRUE FALSE
Alternatif olarak, all.equal
daha fazla işlev çağrısını sarmak yerine, yalnızca ilgili all.equal.numeric
içleri çoğaltabilir ve örtük vektörleştirmeyi kullanabilirsiniz:
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
Bu, dplyr::near
kendini şu şekilde belgeleyen yaklaşımdır .
Kayan nokta sayılarının iki vektörünün (çift olarak) eşit olup olmadığını karşılaştırmanın güvenli bir yoludur. Kullanmaktan daha güvenlidir
==
, çünkü yerleşik bir toleransı vardır
dplyr::near(a, b)
#[1] TRUE TRUE TRUE FALSE
Brian'ın yorumuna (bunun nedeni) ekleyerek all.equal
bunun yerine şunu kullanabilirsiniz:
# 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
Burada Joshua'nın uyarısı güncellenmiş kod (Teşekkürler 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
all.equal
FALSE
farklılıklar olduğunda geri dönmez , bu nedenle isTRUE
bir if
ifadede kullanırken onu sarmanız gerekir .
Bu acayip, ama hızlı:
if(round(i, 10)==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
all.equal(... tolerance)
parametreyi kullanabilirsiniz . all.equal(0.147, 0.15, tolerance=0.05)
doğru.
dplyr::near()
kayan nokta sayılarının iki vektörünün eşit olup olmadığını test etmek için bir seçenektir. Bu dokümanlardan bir örnek :
sqrt(2) ^ 2 == 2
#> [1] FALSE
library(dplyr)
near(sqrt(2) ^ 2, 2)
#> [1] TRUE
Fonksiyonun yerleşik bir tolerans parametresi vardır: tol = .Machine$double.eps^0.5
ayarlanabilir. Varsayılan parametre, için varsayılanla aynıdır all.equal()
.
Benzer bir sorun yaşadım. Aşağıdaki çözümü kullandım.
@ Eşitsiz kesim aralıklarıyla ilgili bu çözümü çözüm buldum. @ R'de yuvarlak işlevi kullandım. Seçeneği 2 rakama ayarlayarak sorunu çözmedim.
options(digits = 2)
cbind(
seq( from = 1, to = 9, by = 1 ),
cut( seq( from = 1, to = 9, by = 1), c( 0, 3, 6, 9 ) ),
seq( from = 0.1, to = 0.9, by = 0.1 ),
cut( seq( from = 0.1, to = 0.9, by = 0.1), c( 0, 0.3, 0.6, 0.9 )),
seq( from = 0.01, to = 0.09, by = 0.01 ),
cut( seq( from = 0.01, to = 0.09, by = 0.01), c( 0, 0.03, 0.06, 0.09 ))
)
seçeneklere göre eşit olmayan kesme aralıklarının çıkışı (basamak = 2):
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 0.1 1 0.01 1
[2,] 2 1 0.2 1 0.02 1
[3,] 3 1 0.3 2 0.03 1
[4,] 4 2 0.4 2 0.04 2
[5,] 5 2 0.5 2 0.05 2
[6,] 6 2 0.6 2 0.06 3
[7,] 7 3 0.7 3 0.07 3
[8,] 8 3 0.8 3 0.08 3
[9,] 9 3 0.9 3 0.09 3
options(digits = 200)
cbind(
seq( from = 1, to = 9, by = 1 ),
cut( round(seq( from = 1, to = 9, by = 1), 2), c( 0, 3, 6, 9 ) ),
seq( from = 0.1, to = 0.9, by = 0.1 ),
cut( round(seq( from = 0.1, to = 0.9, by = 0.1), 2), c( 0, 0.3, 0.6, 0.9 )),
seq( from = 0.01, to = 0.09, by = 0.01 ),
cut( round(seq( from = 0.01, to = 0.09, by = 0.01), 2), c( 0, 0.03, 0.06, 0.09 ))
)
yuvarlak fonksiyona dayalı eşit kesme aralıklarının çıkışı:
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 1 0.1 1 0.01 1
[2,] 2 1 0.2 1 0.02 1
[3,] 3 1 0.3 1 0.03 1
[4,] 4 2 0.4 2 0.04 2
[5,] 5 2 0.5 2 0.05 2
[6,] 6 2 0.6 2 0.06 2
[7,] 7 3 0.7 3 0.07 3
[8,] 8 3 0.8 3 0.08 3
[9,] 9 3 0.9 3 0.09 3
Çift önlü aritmetikte genelleştirilmiş karşılaştırmalar ("<=", "> =", "="):
<= B karşılaştırması:
IsSmallerOrEqual <- function(a,b) {
# Control the existence of "Mean relative difference..." in all.equal;
# 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
IsSmallerOrEqual(abs(-2-(-2.2)), 0.3) # TRUE
IsSmallerOrEqual(abs(-2-(-2.2)), 0.1) # FALSE
IsSmallerOrEqual(3,3); IsSmallerOrEqual(3,4); IsSmallerOrEqual(4,3)
# TRUE; TRUE; FALSE
> = B karşılaştırması:
IsBiggerOrEqual <- function(a,b) {
# Control the existence of "Mean relative difference..." in all.equal;
# 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); IsBiggerOrEqual(4,3); IsBiggerOrEqual(3,4)
# TRUE; TRUE; FALSE
A = b karşılaştırması:
IsEqual <- function(a,b) {
# Control the existence of "Mean relative difference..." in all.equal;
# if exists, it results in character, not logical:
if ( class(all.equal(a, b)) == "logical" ) { return(TRUE)
} else { return(FALSE) }
}
IsEqual(0.1+0.05,0.15) # TRUE